mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-11-17 03:46:00 +01:00
Session: Added support for pointers and keyboards
Via libinput, of course.
This commit is contained in:
parent
3e2fe425e4
commit
1e4fd47865
2 changed files with 370 additions and 16 deletions
|
@ -3,16 +3,21 @@
|
|||
#include <sys/types.h>
|
||||
#include <hyprutils/signal/Signal.hpp>
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include "../input/Input.hpp"
|
||||
#include <vector>
|
||||
|
||||
struct udev;
|
||||
struct udev_monitor;
|
||||
struct udev_device;
|
||||
struct libseat;
|
||||
struct libinput;
|
||||
struct libinput_event;
|
||||
struct libinput_device;
|
||||
|
||||
namespace Aquamarine {
|
||||
class CBackend;
|
||||
class CSession;
|
||||
class CLibinputDevice;
|
||||
|
||||
class CSessionDevice {
|
||||
public:
|
||||
|
@ -50,6 +55,50 @@ namespace Aquamarine {
|
|||
Hyprutils::Memory::CWeakPointer<CSession> session;
|
||||
};
|
||||
|
||||
class CLibinputKeyboard : public IKeyboard {
|
||||
public:
|
||||
CLibinputKeyboard(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
|
||||
virtual ~CLibinputKeyboard() {
|
||||
;
|
||||
}
|
||||
|
||||
virtual libinput_device* getLibinputHandle();
|
||||
virtual const std::string& getName();
|
||||
virtual void updateLEDs(uint32_t leds);
|
||||
|
||||
private:
|
||||
Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;
|
||||
};
|
||||
|
||||
class CLibinputMouse : public IPointer {
|
||||
public:
|
||||
CLibinputMouse(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
|
||||
virtual ~CLibinputMouse() {
|
||||
;
|
||||
}
|
||||
|
||||
virtual libinput_device* getLibinputHandle();
|
||||
virtual const std::string& getName();
|
||||
|
||||
private:
|
||||
Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;
|
||||
};
|
||||
|
||||
class CLibinputDevice {
|
||||
public:
|
||||
CLibinputDevice(libinput_device* device, Hyprutils::Memory::CWeakPointer<CSession> session_);
|
||||
~CLibinputDevice();
|
||||
|
||||
void init();
|
||||
|
||||
libinput_device* device;
|
||||
Hyprutils::Memory::CWeakPointer<CLibinputDevice> self;
|
||||
Hyprutils::Memory::CWeakPointer<CSession> session;
|
||||
std::string name;
|
||||
|
||||
Hyprutils::Memory::CSharedPointer<CLibinputKeyboard> keyboard;
|
||||
Hyprutils::Memory::CSharedPointer<CLibinputMouse> mouse;
|
||||
};
|
||||
class CSession {
|
||||
public:
|
||||
~CSession();
|
||||
|
@ -59,16 +108,20 @@ namespace Aquamarine {
|
|||
bool active = true; // whether the current vt is ours
|
||||
uint32_t vt = 0; // 0 means unsupported
|
||||
std::string seatName;
|
||||
Hyprutils::Memory::CWeakPointer<CSession> self;
|
||||
|
||||
std::vector<Hyprutils::Memory::CSharedPointer<CSessionDevice>> sessionDevices;
|
||||
std::vector<Hyprutils::Memory::CSharedPointer<CLibinputDevice>> libinputDevices;
|
||||
|
||||
udev* udevHandle = nullptr;
|
||||
udev_monitor* udevMonitor = nullptr;
|
||||
libseat* libseatHandle = nullptr;
|
||||
libinput* libinputHandle = nullptr;
|
||||
|
||||
std::vector<int> pollFDs();
|
||||
void dispatchPendingEventsAsync();
|
||||
bool switchVT(uint32_t vt);
|
||||
void onReady();
|
||||
|
||||
struct SAddDrmCardEvent {
|
||||
std::string path;
|
||||
|
@ -84,7 +137,10 @@ namespace Aquamarine {
|
|||
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
||||
|
||||
void dispatchUdevEvents();
|
||||
void dispatchLibinputEvents();
|
||||
void handleLibinputEvent(libinput_event* e);
|
||||
|
||||
friend class CSessionDevice;
|
||||
friend class CLibinputDevice;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include <libseat.h>
|
||||
#include <libinput.h>
|
||||
#include <libudev.h>
|
||||
#include <cstring>
|
||||
#include <xf86drm.h>
|
||||
|
@ -13,12 +14,15 @@ using namespace Aquamarine;
|
|||
using namespace Hyprutils::Memory;
|
||||
#define SP CSharedPointer
|
||||
|
||||
// we can't really do better with libseat logs
|
||||
static const std::string AQ_UNKNOWN_DEVICE_NAME = "UNKNOWN";
|
||||
|
||||
// we can't really do better with libseat/libinput logs
|
||||
// because they don't allow us to pass "data" or anything...
|
||||
// Nobody should create multiple backends anyways really
|
||||
Hyprutils::Memory::CSharedPointer<CBackend> backendInUse;
|
||||
|
||||
static Aquamarine::eBackendLogLevel logLevelFromLibseat(enum libseat_log_level level) {
|
||||
//
|
||||
static Aquamarine::eBackendLogLevel logLevelFromLibseat(libseat_log_level level) {
|
||||
switch (level) {
|
||||
case LIBSEAT_LOG_LEVEL_ERROR: return AQ_LOG_ERROR;
|
||||
case LIBSEAT_LOG_LEVEL_SILENT: return AQ_LOG_TRACE;
|
||||
|
@ -28,6 +32,15 @@ static Aquamarine::eBackendLogLevel logLevelFromLibseat(enum libseat_log
|
|||
return AQ_LOG_DEBUG;
|
||||
}
|
||||
|
||||
static Aquamarine::eBackendLogLevel logLevelFromLibinput(libinput_log_priority level) {
|
||||
switch (level) {
|
||||
case LIBINPUT_LOG_PRIORITY_ERROR: return AQ_LOG_ERROR;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return AQ_LOG_DEBUG;
|
||||
}
|
||||
|
||||
static void libseatLog(libseat_log_level level, const char* fmt, va_list args) {
|
||||
if (!backendInUse)
|
||||
return;
|
||||
|
@ -38,24 +51,69 @@ static void libseatLog(libseat_log_level level, const char* fmt, va_list args) {
|
|||
backendInUse->log(logLevelFromLibseat(level), std::format("[libseat] {}", string));
|
||||
}
|
||||
|
||||
static void libinputLog(libinput*, libinput_log_priority level, const char* fmt, va_list args) {
|
||||
if (!backendInUse)
|
||||
return;
|
||||
|
||||
static char string[1024];
|
||||
vsnprintf(string, sizeof(string), fmt, args);
|
||||
|
||||
backendInUse->log(logLevelFromLibinput(level), std::format("[libinput] {}", string));
|
||||
}
|
||||
|
||||
// ------------ Libseat
|
||||
|
||||
static void libseatEnableSeat(struct libseat* seat, void* data) {
|
||||
auto PSESSION = (Aquamarine::CSession*)data;
|
||||
PSESSION->active = true;
|
||||
libinput_resume(PSESSION->libinputHandle);
|
||||
PSESSION->events.changeActive.emit();
|
||||
}
|
||||
|
||||
static void libseatDisableSeat(struct libseat* seat, void* data) {
|
||||
auto PSESSION = (Aquamarine::CSession*)data;
|
||||
PSESSION->active = false;
|
||||
libinput_suspend(PSESSION->libinputHandle);
|
||||
PSESSION->events.changeActive.emit();
|
||||
libseat_disable_seat(PSESSION->libseatHandle);
|
||||
}
|
||||
|
||||
static const libseat_seat_listener libseatListener = {
|
||||
.enable_seat = libseatEnableSeat,
|
||||
.disable_seat = libseatDisableSeat,
|
||||
.enable_seat = ::libseatEnableSeat,
|
||||
.disable_seat = ::libseatDisableSeat,
|
||||
};
|
||||
|
||||
// ------------ Libinput
|
||||
|
||||
static int libinputOpen(const char* path, int flags, void* data) {
|
||||
auto SESSION = (CSession*)data;
|
||||
|
||||
auto dev = makeShared<CSessionDevice>(SESSION->self.lock(), path);
|
||||
if (!dev->dev)
|
||||
return -1;
|
||||
|
||||
SESSION->sessionDevices.emplace_back(dev);
|
||||
return dev->fd;
|
||||
}
|
||||
|
||||
static void libinputClose(int fd, void* data) {
|
||||
auto SESSION = (CSession*)data;
|
||||
|
||||
std::erase_if(SESSION->sessionDevices, [fd](const auto& dev) {
|
||||
auto toRemove = dev->fd == fd;
|
||||
if (toRemove)
|
||||
dev->events.remove.emit();
|
||||
return toRemove;
|
||||
});
|
||||
}
|
||||
|
||||
static const libinput_interface libinputListener = {
|
||||
.open_restricted = ::libinputOpen,
|
||||
.close_restricted = ::libinputClose,
|
||||
};
|
||||
|
||||
// ------------
|
||||
|
||||
Aquamarine::CSessionDevice::CSessionDevice(Hyprutils::Memory::CSharedPointer<CSession> session_, const std::string& path_) : session(session_), path(path_) {
|
||||
deviceID = libseat_open_device(session->libseatHandle, path.c_str(), &fd);
|
||||
if (deviceID < 0) {
|
||||
|
@ -107,6 +165,7 @@ SP<CSession> Aquamarine::CSession::attempt(Hyprutils::Memory::CSharedPointer<CBa
|
|||
|
||||
auto session = makeShared<CSession>();
|
||||
session->backend = backend_;
|
||||
session->self = session;
|
||||
backendInUse = backend_;
|
||||
|
||||
// ------------ Libseat
|
||||
|
@ -149,12 +208,30 @@ SP<CSession> Aquamarine::CSession::attempt(Hyprutils::Memory::CSharedPointer<CBa
|
|||
udev_monitor_filter_add_match_subsystem_devtype(session->udevMonitor, "drm", nullptr);
|
||||
udev_monitor_enable_receiving(session->udevMonitor);
|
||||
|
||||
// ----------- Libinput
|
||||
|
||||
session->libinputHandle = libinput_udev_create_context(&libinputListener, session.get(), session->udevHandle);
|
||||
if (!session->libinputHandle) {
|
||||
session->backend->log(AQ_LOG_ERROR, "libinput: failed to create a new context");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (libinput_udev_assign_seat(session->libinputHandle, session->seatName.c_str())) {
|
||||
session->backend->log(AQ_LOG_ERROR, "libinput: failed to assign a seat");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
libinput_log_set_handler(session->libinputHandle, ::libinputLog);
|
||||
libinput_log_set_priority(session->libinputHandle, LIBINPUT_LOG_PRIORITY_DEBUG);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
Aquamarine::CSession::~CSession() {
|
||||
sessionDevices.clear();
|
||||
|
||||
if (libinputHandle)
|
||||
libinput_unref(libinputHandle);
|
||||
if (libseatHandle)
|
||||
libseat_close_seat(libseatHandle);
|
||||
if (udevMonitor)
|
||||
|
@ -180,6 +257,17 @@ static bool isDRMCard(const char* sysname) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Aquamarine::CSession::onReady() {
|
||||
for (auto& d : libinputDevices) {
|
||||
if (d->keyboard)
|
||||
backend->events.newKeyboard.emit(SP<IKeyboard>(d->keyboard));
|
||||
if (d->mouse)
|
||||
backend->events.newPointer.emit(SP<IPointer>(d->mouse));
|
||||
|
||||
// FIXME: other devices.
|
||||
}
|
||||
}
|
||||
|
||||
void Aquamarine::CSession::dispatchUdevEvents() {
|
||||
auto device = udev_monitor_receive_device(udevMonitor);
|
||||
|
||||
|
@ -243,20 +331,230 @@ void Aquamarine::CSession::dispatchUdevEvents() {
|
|||
return;
|
||||
}
|
||||
|
||||
void Aquamarine::CSession::dispatchLibinputEvents() {
|
||||
if (int ret = libinput_dispatch(libinputHandle); ret) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("Couldn't dispatch libinput events: {}", strerror(-ret)));
|
||||
return;
|
||||
}
|
||||
|
||||
libinput_event* event = libinput_get_event(libinputHandle);
|
||||
while (event) {
|
||||
handleLibinputEvent(event);
|
||||
libinput_event_destroy(event);
|
||||
event = libinput_get_event(libinputHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void Aquamarine::CSession::dispatchPendingEventsAsync() {
|
||||
if (libseat_dispatch(libseatHandle, 0) == -1)
|
||||
backend->log(AQ_LOG_ERROR, "Couldn't dispatch libseat events");
|
||||
|
||||
dispatchUdevEvents();
|
||||
dispatchLibinputEvents();
|
||||
}
|
||||
|
||||
std::vector<int> Aquamarine::CSession::pollFDs() {
|
||||
if (!libseatHandle || !udevMonitor || !udevHandle)
|
||||
return {};
|
||||
|
||||
return {libseat_get_fd(libseatHandle), udev_monitor_get_fd(udevMonitor)};
|
||||
return {libseat_get_fd(libseatHandle), udev_monitor_get_fd(udevMonitor), libinput_get_fd(libinputHandle)};
|
||||
}
|
||||
|
||||
bool Aquamarine::CSession::switchVT(uint32_t vt) {
|
||||
return libseat_switch_session(libseatHandle, vt) == 0;
|
||||
}
|
||||
|
||||
void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
|
||||
auto device = libinput_event_get_device(e);
|
||||
auto eventType = libinput_event_get_type(e);
|
||||
auto data = libinput_device_get_user_data(device);
|
||||
|
||||
if (!data && eventType != LIBINPUT_EVENT_DEVICE_ADDED) {
|
||||
backend->log(AQ_LOG_ERROR, "libinput: No aq device in event and not added");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
auto dev = libinputDevices.emplace_back(makeShared<CLibinputDevice>(device, self));
|
||||
dev->self = dev;
|
||||
dev->init();
|
||||
return;
|
||||
}
|
||||
|
||||
auto hlDevice = ((CLibinputDevice*)data)->self.lock();
|
||||
|
||||
switch (eventType) {
|
||||
case LIBINPUT_EVENT_DEVICE_ADDED:
|
||||
/* shouldn't happen */
|
||||
break;
|
||||
case LIBINPUT_EVENT_DEVICE_REMOVED:
|
||||
std::erase_if(libinputDevices, [device](const auto& d) { return d->device == device; });
|
||||
break;
|
||||
|
||||
// --------- keyboard
|
||||
|
||||
case LIBINPUT_EVENT_KEYBOARD_KEY: {
|
||||
auto kbe = libinput_event_get_keyboard_event(e);
|
||||
hlDevice->keyboard->events.key.emit(IKeyboard::SKeyEvent{
|
||||
.timeMs = (uint32_t)(libinput_event_keyboard_get_time_usec(kbe) / 1000),
|
||||
.key = libinput_event_keyboard_get_key(kbe),
|
||||
.pressed = libinput_event_keyboard_get_key_state(kbe) == LIBINPUT_KEY_STATE_PRESSED,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// --------- pointer
|
||||
|
||||
case LIBINPUT_EVENT_POINTER_MOTION: {
|
||||
auto pe = libinput_event_get_pointer_event(e);
|
||||
hlDevice->mouse->events.move.emit(IPointer::SMoveEvent{
|
||||
.timeMs = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
|
||||
.delta = {libinput_event_pointer_get_dx(pe), libinput_event_pointer_get_dy(pe)},
|
||||
.unaccel = {libinput_event_pointer_get_dx_unaccelerated(pe), libinput_event_pointer_get_dy_unaccelerated(pe)},
|
||||
});
|
||||
hlDevice->mouse->events.frame.emit();
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
|
||||
auto pe = libinput_event_get_pointer_event(e);
|
||||
hlDevice->mouse->events.warp.emit(IPointer::SWarpEvent{
|
||||
.timeMs = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
|
||||
.absolute = {libinput_event_pointer_get_absolute_x_transformed(pe, 1), libinput_event_pointer_get_absolute_y_transformed(pe, 1)},
|
||||
});
|
||||
hlDevice->mouse->events.frame.emit();
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBINPUT_EVENT_POINTER_BUTTON: {
|
||||
auto pe = libinput_event_get_pointer_event(e);
|
||||
const auto SEATCOUNT = libinput_event_pointer_get_seat_button_count(pe);
|
||||
const bool PRESSED = libinput_event_pointer_get_button_state(pe) == LIBINPUT_BUTTON_STATE_PRESSED;
|
||||
|
||||
if ((PRESSED && SEATCOUNT != 1) || (!PRESSED && SEATCOUNT != 0))
|
||||
break;
|
||||
|
||||
hlDevice->mouse->events.button.emit(IPointer::SButtonEvent{
|
||||
.timeMs = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
|
||||
.button = libinput_event_pointer_get_button(pe),
|
||||
.pressed = PRESSED,
|
||||
});
|
||||
hlDevice->mouse->events.frame.emit();
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: {
|
||||
auto pe = libinput_event_get_pointer_event(e);
|
||||
|
||||
IPointer::SAxisEvent aqe = {
|
||||
.timeMs = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
|
||||
};
|
||||
|
||||
switch (eventType) {
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_WHEEL;
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_FINGER: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_FINGER; break;
|
||||
case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_CONTINUOUS; break;
|
||||
default: break; /* unreachable */
|
||||
}
|
||||
|
||||
static const std::array<libinput_pointer_axis, 2> LAXES = {
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
|
||||
};
|
||||
|
||||
for (auto& axis : LAXES) {
|
||||
if (!libinput_event_pointer_has_axis(pe, axis))
|
||||
continue;
|
||||
|
||||
aqe.axis = axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL ? IPointer::AQ_POINTER_AXIS_VERTICAL : IPointer::AQ_POINTER_AXIS_HORIZONTAL;
|
||||
aqe.delta = libinput_event_pointer_get_axis_value(pe, axis);
|
||||
aqe.direction = IPointer::AQ_POINTER_AXIS_RELATIVE_IDENTICAL;
|
||||
if (libinput_device_config_scroll_get_natural_scroll_enabled(device))
|
||||
aqe.direction = IPointer::AQ_POINTER_AXIS_RELATIVE_INVERTED;
|
||||
|
||||
if (aqe.source == IPointer::AQ_POINTER_AXIS_SOURCE_WHEEL)
|
||||
aqe.discrete = libinput_event_pointer_get_scroll_value_v120(pe, axis);
|
||||
|
||||
hlDevice->mouse->events.axis.emit(aqe);
|
||||
}
|
||||
|
||||
hlDevice->mouse->events.frame.emit();
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: other events
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
Aquamarine::CLibinputDevice::CLibinputDevice(libinput_device* device_, Hyprutils::Memory::CWeakPointer<CSession> session_) : device(device_), session(session_) {
|
||||
;
|
||||
}
|
||||
|
||||
void Aquamarine::CLibinputDevice::init() {
|
||||
const auto VENDOR = libinput_device_get_id_vendor(device);
|
||||
const auto PRODUCT = libinput_device_get_id_product(device);
|
||||
const auto NAME = libinput_device_get_name(device);
|
||||
|
||||
session->backend->log(AQ_LOG_DEBUG, std::format("libinput: New device {}: {}-{}", NAME ? NAME : "Unknown", VENDOR, PRODUCT));
|
||||
|
||||
name = NAME;
|
||||
|
||||
libinput_device_ref(device);
|
||||
libinput_device_set_user_data(device, this);
|
||||
|
||||
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
|
||||
keyboard = makeShared<CLibinputKeyboard>(self.lock());
|
||||
if (session->backend->ready)
|
||||
session->backend->events.newKeyboard.emit(SP<IKeyboard>(keyboard));
|
||||
}
|
||||
|
||||
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
|
||||
mouse = makeShared<CLibinputMouse>(self.lock());
|
||||
if (session->backend->ready)
|
||||
session->backend->events.newPointer.emit(SP<IPointer>(mouse));
|
||||
}
|
||||
|
||||
// FIXME: other devices
|
||||
}
|
||||
|
||||
Aquamarine::CLibinputDevice::~CLibinputDevice() {
|
||||
libinput_device_unref(device);
|
||||
}
|
||||
|
||||
Aquamarine::CLibinputKeyboard::CLibinputKeyboard(SP<CLibinputDevice> dev) : device(dev) {}
|
||||
|
||||
libinput_device* Aquamarine::CLibinputKeyboard::getLibinputHandle() {
|
||||
if (!device)
|
||||
return nullptr;
|
||||
return device->device;
|
||||
}
|
||||
|
||||
const std::string& Aquamarine::CLibinputKeyboard::getName() {
|
||||
if (!device)
|
||||
return AQ_UNKNOWN_DEVICE_NAME;
|
||||
return device->name;
|
||||
}
|
||||
|
||||
void Aquamarine::CLibinputKeyboard::updateLEDs(uint32_t leds) {
|
||||
; // FIXME:
|
||||
}
|
||||
|
||||
Aquamarine::CLibinputMouse::CLibinputMouse(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
|
||||
;
|
||||
}
|
||||
|
||||
libinput_device* Aquamarine::CLibinputMouse::getLibinputHandle() {
|
||||
if (!device)
|
||||
return nullptr;
|
||||
return device->device;
|
||||
}
|
||||
|
||||
const std::string& Aquamarine::CLibinputMouse::getName() {
|
||||
if (!device)
|
||||
return AQ_UNKNOWN_DEVICE_NAME;
|
||||
return device->name;
|
||||
}
|
Loading…
Reference in a new issue