Tablet: move to new impl

Ring and strip are not implemented. Will I implement this? God fucking knows. Nobody seems to have that anyways.
This commit is contained in:
Vaxry 2024-05-06 02:15:26 +01:00 committed by Vaxry
parent ed411f53bd
commit 84e8d1810d
21 changed files with 1926 additions and 1606 deletions

View file

@ -251,7 +251,6 @@ target_link_libraries(Hyprland
uuid uuid
) )
protocol("protocols/tablet-unstable-v2.xml" "tablet-unstable-v2" true)
protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true) protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
@ -285,6 +284,7 @@ protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "
protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false) protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false)
protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false) protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false)
protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false)
protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false)
# tools # tools
add_subdirectory(hyprctl) add_subdirectory(hyprctl)

View file

@ -29,7 +29,6 @@ protocols = [
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'], ['wlr-screencopy-unstable-v1.xml'],
['tablet-unstable-v2.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'], [hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml'] [hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml']
] ]
@ -60,6 +59,7 @@ new_protocols = [
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'], [wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'], [wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'], [wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
[wl_protocol_dir, 'stable/tablet/tablet-v2.xml'],
] ]
wl_protos_src = [] wl_protos_src = []

File diff suppressed because it is too large Load diff

View file

@ -242,8 +242,6 @@ void CCompositor::initServer() {
Debug::log(INFO, "VR will not be available"); Debug::log(INFO, "VR will not be available");
} }
// m_sWLRTabletManager = wlr_tablet_v2_create(m_sWLDisplay);
m_sWLRForeignRegistry = wlr_xdg_foreign_registry_create(m_sWLDisplay); m_sWLRForeignRegistry = wlr_xdg_foreign_registry_create(m_sWLDisplay);
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry); wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);

View file

@ -56,7 +56,6 @@ class CCompositor {
wlr_presentation* m_sWLRPresentation; wlr_presentation* m_sWLRPresentation;
wlr_egl* m_sWLREGL; wlr_egl* m_sWLREGL;
int m_iDRMFD; int m_iDRMFD;
wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry; wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf; wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend; wlr_backend* m_sWLRHeadlessBackend;

View file

@ -22,6 +22,7 @@
#include "../devices/IPointer.hpp" #include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp" #include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp" #include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
static void trimTrailingComma(std::string& str) { static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',') if (!str.empty() && str.back() == ',')
@ -557,7 +558,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"tablets\": [\n"; result += "\"tablets\": [\n";
for (auto& d : g_pInputManager->m_lTabletPads) { for (auto& d : g_pInputManager->m_vTabletPads) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -567,26 +568,26 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"name": "{}" "name": "{}"
}} }}
}},)#", }},)#",
(uintptr_t)&d, (uintptr_t)d.pTabletParent, escapeJSONStrings(d.pTabletParent ? d.pTabletParent->name : "")); (uintptr_t)d.get(), (uintptr_t)d->parent.get(), escapeJSONStrings(d->parent ? d->parent->hlName : ""));
} }
for (auto& d : g_pInputManager->m_lTablets) { for (auto& d : g_pInputManager->m_vTablets) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
"name": "{}" "name": "{}"
}},)#", }},)#",
(uintptr_t)&d, escapeJSONStrings(d.name)); (uintptr_t)d.get(), escapeJSONStrings(d->hlName));
} }
for (auto& d : g_pInputManager->m_lTabletTools) { for (auto& d : g_pInputManager->m_vTabletTools) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
"type": "tabletTool", "type": "tabletTool",
"belongsTo": "0x{:x}" "belongsTo": "0x{:x}"
}},)#", }},)#",
(uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0); (uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
} }
trimTrailingComma(result); trimTrailingComma(result);
@ -643,16 +644,16 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nTablets:\n"; result += "\n\nTablets:\n";
for (auto& d : g_pInputManager->m_lTabletPads) { for (auto& d : g_pInputManager->m_vTabletPads) {
result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)&d, (uintptr_t)d.pTabletParent, d.pTabletParent ? d.pTabletParent->name : ""); result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->parent.get(), d->parent ? d->parent->hlName : "");
} }
for (auto& d : g_pInputManager->m_lTablets) { for (auto& d : g_pInputManager->m_vTablets) {
result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)&d, d.name, d.wlrTablet->width_mm, d.wlrTablet->height_mm); result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->wlr()->width_mm, d->wlr()->height_mm);
} }
for (auto& d : g_pInputManager->m_lTabletTools) { for (auto& d : g_pInputManager->m_vTabletTools) {
result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)&d, d.wlrTabletTool ? (uintptr_t)d.wlrTabletTool->data : 0); result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
} }
result += "\n\nTouch:\n"; result += "\n\nTouch:\n";

View file

@ -8,6 +8,7 @@ enum eHIDCapabilityType : uint32_t {
HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0), HID_INPUT_CAPABILITY_KEYBOARD = (1 << 0),
HID_INPUT_CAPABILITY_POINTER = (1 << 1), HID_INPUT_CAPABILITY_POINTER = (1 << 1),
HID_INPUT_CAPABILITY_TOUCH = (1 << 2), HID_INPUT_CAPABILITY_TOUCH = (1 << 2),
HID_INPUT_CAPABILITY_TABLET = (1 << 3),
}; };
enum eHIDType { enum eHIDType {
@ -16,6 +17,8 @@ enum eHIDType {
HID_TYPE_KEYBOARD, HID_TYPE_KEYBOARD,
HID_TYPE_TOUCH, HID_TYPE_TOUCH,
HID_TYPE_TABLET, HID_TYPE_TABLET,
HID_TYPE_TABLET_TOOL,
HID_TYPE_TABLET_PAD,
}; };
/* /*

326
src/devices/Tablet.cpp Normal file
View file

@ -0,0 +1,326 @@
#include "Tablet.hpp"
#include "../defines.hpp"
#include "../protocols/Tablet.hpp"
SP<CTablet> CTablet::create(wlr_tablet* tablet) {
SP<CTablet> pTab = SP<CTablet>(new CTablet(tablet));
pTab->self = pTab;
PROTO::tablet->registerDevice(pTab);
return pTab;
}
SP<CTabletTool> CTabletTool::create(wlr_tablet_tool* tablet) {
SP<CTabletTool> pTab = SP<CTabletTool>(new CTabletTool(tablet));
pTab->self = pTab;
PROTO::tablet->registerDevice(pTab);
return pTab;
}
SP<CTabletPad> CTabletPad::create(wlr_tablet_pad* tablet) {
SP<CTabletPad> pTab = SP<CTabletPad>(new CTabletPad(tablet));
pTab->self = pTab;
PROTO::tablet->registerDevice(pTab);
return pTab;
}
SP<CTabletTool> CTabletTool::fromWlr(wlr_tablet_tool* tool) {
return ((CTabletTool*)tool->data)->self.lock();
}
SP<CTablet> CTablet::fromWlr(wlr_tablet* tablet) {
return ((CTablet*)tablet->data)->self.lock();
}
static uint32_t wlrUpdateToHl(uint32_t wlr) {
uint32_t result = 0;
if (wlr & WLR_TABLET_TOOL_AXIS_X)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X;
if (wlr & WLR_TABLET_TOOL_AXIS_Y)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y;
if (wlr & WLR_TABLET_TOOL_AXIS_DISTANCE)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_DISTANCE;
if (wlr & WLR_TABLET_TOOL_AXIS_PRESSURE)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE;
if (wlr & WLR_TABLET_TOOL_AXIS_TILT_X)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X;
if (wlr & WLR_TABLET_TOOL_AXIS_TILT_Y)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y;
if (wlr & WLR_TABLET_TOOL_AXIS_ROTATION)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_ROTATION;
if (wlr & WLR_TABLET_TOOL_AXIS_SLIDER)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_SLIDER;
if (wlr & WLR_TABLET_TOOL_AXIS_WHEEL)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_WHEEL;
return result;
}
uint32_t CTablet::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet* CTablet::wlr() {
return tablet;
}
CTablet::CTablet(wlr_tablet* tablet_) : tablet(tablet_) {
if (!tablet)
return;
tablet->data = this;
// clang-format off
hyprListener_destroy.initCallback(&tablet->base.events.destroy, [this] (void* owner, void* data) {
tablet = nullptr;
disconnectCallbacks();
events.destroy.emit();
}, this, "CTablet");
hyprListener_axis.initCallback(&tablet->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_axis_event*)data;
tabletEvents.axis.emit(SAxisEvent{
.tool = E->tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.updatedAxes = wlrUpdateToHl(E->updated_axes),
.axis = {E->x, E->y},
.axisDelta = {E->dx, E->dy},
.tilt = {E->tilt_x, E->tilt_y},
.pressure = E->pressure,
.distance = E->distance,
.rotation = E->rotation,
.slider = E->slider,
.wheelDelta = E->wheel_delta,
});
}, this, "CTablet");
hyprListener_proximity.initCallback(&tablet->events.proximity, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_proximity_event*)data;
tabletEvents.proximity.emit(SProximityEvent{
.tool = E->tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.proximity = {E->x, E->y},
.in = E->state == WLR_TABLET_TOOL_PROXIMITY_IN,
});
}, this, "CTablet");
hyprListener_tip.initCallback(&tablet->events.tip, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_tip_event*)data;
tabletEvents.tip.emit(STipEvent{
.tool = E->tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.tip = {E->x, E->y},
.in = E->state == WLR_TABLET_TOOL_TIP_DOWN,
});
}, this, "CTablet");
hyprListener_button.initCallback(&tablet->events.button, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_button_event*)data;
tabletEvents.button.emit(SButtonEvent{
.tool = E->tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.button = E->button,
.down = E->state == WLR_BUTTON_PRESSED,
});
}, this, "CTablet");
// clang-format on
deviceName = tablet->base.name ? tablet->base.name : "UNKNOWN";
}
CTablet::~CTablet() {
if (tablet)
tablet->data = nullptr;
PROTO::tablet->recheckRegisteredDevices();
}
void CTablet::disconnectCallbacks() {
hyprListener_axis.removeCallback();
hyprListener_button.removeCallback();
hyprListener_destroy.removeCallback();
hyprListener_proximity.removeCallback();
hyprListener_tip.removeCallback();
}
eHIDType CTablet::getType() {
return HID_TYPE_TABLET;
}
uint32_t CTabletPad::getCapabilities() {
return HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet_pad* CTabletPad::wlr() {
return pad;
}
eHIDType CTabletPad::getType() {
return HID_TYPE_TABLET_PAD;
}
CTabletPad::CTabletPad(wlr_tablet_pad* pad_) : pad(pad_) {
if (!pad)
return;
// clang-format off
hyprListener_destroy.initCallback(&pad->base.events.destroy, [this] (void* owner, void* data) {
pad = nullptr;
disconnectCallbacks();
events.destroy.emit();
}, this, "CTabletPad");
hyprListener_button.initCallback(&pad->events.button, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_button_event*)data;
padEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.down = E->state == WLR_BUTTON_PRESSED,
.mode = E->mode,
.group = E->group,
});
}, this, "CTabletPad");
hyprListener_ring.initCallback(&pad->events.ring, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_ring_event*)data;
padEvents.ring.emit(SRingEvent{
.timeMs = E->time_msec,
.finger = E->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
.ring = E->ring,
.position = E->position,
.mode = E->mode,
});
}, this, "CTabletPad");
hyprListener_strip.initCallback(&pad->events.strip, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_strip_event*)data;
padEvents.strip.emit(SStripEvent{
.timeMs = E->time_msec,
.finger = E->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
.strip = E->strip,
.position = E->position,
.mode = E->mode,
});
}, this, "CTabletPad");
hyprListener_attach.initCallback(&pad->events.attach_tablet, [this] (void* owner, void* data) {
if (!data)
return;
padEvents.attach.emit(CTabletTool::fromWlr((wlr_tablet_tool*)data));
}, this, "CTabletPad");
// clang-format on
deviceName = pad->base.name ? pad->base.name : "UNKNOWN";
}
CTabletPad::~CTabletPad() {
PROTO::tablet->recheckRegisteredDevices();
}
void CTabletPad::disconnectCallbacks() {
hyprListener_ring.removeCallback();
hyprListener_button.removeCallback();
hyprListener_destroy.removeCallback();
hyprListener_strip.removeCallback();
hyprListener_attach.removeCallback();
}
uint32_t CTabletTool::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet_tool* CTabletTool::wlr() {
return tool;
}
eHIDType CTabletTool::getType() {
return HID_TYPE_TABLET_TOOL;
}
CTabletTool::CTabletTool(wlr_tablet_tool* tool_) : tool(tool_) {
if (!tool)
return;
// clang-format off
hyprListener_destroy.initCallback(&tool->events.destroy, [this] (void* owner, void* data) {
tool = nullptr;
disconnectCallbacks();
events.destroy.emit();
}, this, "CTabletTool");
// clang-format on
if (tool->tilt)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_TILT;
if (tool->pressure)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_PRESSURE;
if (tool->distance)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_DISTANCE;
if (tool->rotation)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_ROTATION;
if (tool->slider)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_SLIDER;
if (tool->wheel)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_WHEEL;
tool->data = this;
deviceName = std::to_string(tool->hardware_serial) + std::to_string(tool->hardware_wacom);
}
CTabletTool::~CTabletTool() {
if (tool)
tool->data = nullptr;
PROTO::tablet->recheckRegisteredDevices();
}
void CTabletTool::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_destroySurface.removeCallback();
}
wlr_surface* CTabletTool::getSurface() {
return pSurface;
}
void CTabletTool::setSurface(wlr_surface* surf) {
if (surf == pSurface)
return;
if (pSurface) {
hyprListener_destroySurface.removeCallback();
pSurface = nullptr;
}
pSurface = surf;
if (surf) {
hyprListener_destroySurface.initCallback(
&surf->events.destroy,
[this](void* owner, void* data) {
PROTO::tablet->proximityOut(self.lock());
pSurface = nullptr;
hyprListener_destroySurface.removeCallback();
},
this, "CTabletTool");
}
}

228
src/devices/Tablet.hpp Normal file
View file

@ -0,0 +1,228 @@
#pragma once
#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/Vector2D.hpp"
#include "../helpers/Box.hpp"
struct wlr_tablet;
struct wlr_tablet_tool;
struct wlr_tablet_pad;
class CTabletTool;
class CTabletPad;
/*
A tablet device
Tablets don't have an interface for now,
if there will be a need it's trivial to do.
*/
class CTablet : public IHID {
public:
static SP<CTablet> create(wlr_tablet* tablet);
static SP<CTablet> fromWlr(wlr_tablet* tablet);
~CTablet();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
wlr_tablet* wlr();
enum eTabletToolAxes {
HID_TABLET_TOOL_AXIS_X = (1 << 0),
HID_TABLET_TOOL_AXIS_Y = (1 << 1),
HID_TABLET_TOOL_AXIS_TILT_X = (1 << 2),
HID_TABLET_TOOL_AXIS_TILT_Y = (1 << 3),
HID_TABLET_TOOL_AXIS_DISTANCE = (1 << 4),
HID_TABLET_TOOL_AXIS_PRESSURE = (1 << 5),
HID_TABLET_TOOL_AXIS_ROTATION = (1 << 6),
HID_TABLET_TOOL_AXIS_SLIDER = (1 << 7),
HID_TABLET_TOOL_AXIS_WHEEL = (1 << 8),
};
struct SAxisEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
uint32_t updatedAxes = 0; // eTabletToolAxes
Vector2D axis;
Vector2D axisDelta;
Vector2D tilt;
double pressure = 0;
double distance = 0;
double rotation = 0;
double slider = 0;
double wheelDelta = 0;
};
struct SProximityEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
Vector2D proximity;
bool in = false;
};
struct STipEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
Vector2D tip;
bool in = false;
};
struct SButtonEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
uint32_t button;
bool down = false;
};
struct {
CSignal axis;
CSignal proximity;
CSignal tip;
CSignal button;
} tabletEvents;
WP<CTablet> self;
bool relativeInput = false;
std::string hlName = "";
std::string boundOutput = "";
CBox activeArea;
CBox boundBox; // output-local
private:
CTablet(wlr_tablet* tablet);
void disconnectCallbacks();
wlr_tablet* tablet = nullptr;
DYNLISTENER(destroy);
DYNLISTENER(axis);
DYNLISTENER(proximity);
DYNLISTENER(tip);
DYNLISTENER(button);
};
class CTabletPad : public IHID {
public:
static SP<CTabletPad> create(wlr_tablet_pad* pad);
~CTabletPad();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
wlr_tablet_pad* wlr();
struct SButtonEvent {
uint32_t timeMs = 0;
uint32_t button = 0;
bool down = false;
uint32_t mode = 0;
uint32_t group = 0;
};
struct SRingEvent {
uint32_t timeMs = 0;
bool finger = false;
uint32_t ring = 0;
double position = 0;
uint32_t mode = 0;
};
struct SStripEvent {
uint32_t timeMs = 0;
bool finger = false;
uint32_t strip = 0;
double position = 0;
uint32_t mode = 0;
};
struct {
CSignal button;
CSignal ring;
CSignal strip;
CSignal attach;
} padEvents;
WP<CTabletPad> self;
WP<CTabletTool> parent;
std::string hlName;
private:
CTabletPad(wlr_tablet_pad* pad);
void disconnectCallbacks();
wlr_tablet_pad* pad = nullptr;
DYNLISTENER(destroy);
DYNLISTENER(ring);
DYNLISTENER(strip);
DYNLISTENER(button);
DYNLISTENER(attach);
};
class CTabletTool : public IHID {
public:
static SP<CTabletTool> create(wlr_tablet_tool* tool);
static SP<CTabletTool> fromWlr(wlr_tablet_tool* tool);
~CTabletTool();
enum eTabletToolType {
HID_TABLET_TOOL_TYPE_PEN = 1,
HID_TABLET_TOOL_TYPE_ERASER,
HID_TABLET_TOOL_TYPE_BRUSH,
HID_TABLET_TOOL_TYPE_PENCIL,
HID_TABLET_TOOL_TYPE_AIRBRUSH,
HID_TABLET_TOOL_TYPE_MOUSE,
HID_TABLET_TOOL_TYPE_LENS,
HID_TABLET_TOOL_TYPE_TOTEM,
};
enum eTabletToolCapabilities {
HID_TABLET_TOOL_CAPABILITY_TILT = (1 << 0),
HID_TABLET_TOOL_CAPABILITY_PRESSURE = (1 << 1),
HID_TABLET_TOOL_CAPABILITY_DISTANCE = (1 << 2),
HID_TABLET_TOOL_CAPABILITY_ROTATION = (1 << 3),
HID_TABLET_TOOL_CAPABILITY_SLIDER = (1 << 4),
HID_TABLET_TOOL_CAPABILITY_WHEEL = (1 << 5),
};
virtual uint32_t getCapabilities();
wlr_tablet_tool* wlr();
virtual eHIDType getType();
wlr_surface* getSurface();
void setSurface(wlr_surface*);
WP<CTabletTool> self;
Vector2D tilt;
bool active = false; // true if in proximity
uint32_t toolCapabilities = 0;
bool isDown = false;
std::vector<uint32_t> buttonsDown;
Vector2D absolutePos; // last known absolute position.
std::string hlName;
private:
CTabletTool(wlr_tablet_tool* tool);
void disconnectCallbacks();
wlr_surface* pSurface = nullptr;
wlr_tablet_tool* tool = nullptr;
DYNLISTENER(destroy);
DYNLISTENER(destroySurface);
};

View file

@ -38,8 +38,8 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
g_pInputManager->newTouchDevice(DEVICE); g_pInputManager->newTouchDevice(DEVICE);
break; break;
case WLR_INPUT_DEVICE_TABLET: case WLR_INPUT_DEVICE_TABLET:
Debug::log(LOG, "Attached a tablet tool with name {}", DEVICE->name); Debug::log(LOG, "Attached a tablet with name {}", DEVICE->name);
g_pInputManager->newTabletTool(DEVICE); g_pInputManager->newTablet(DEVICE);
break; break;
case WLR_INPUT_DEVICE_TABLET_PAD: case WLR_INPUT_DEVICE_TABLET_PAD:
Debug::log(LOG, "Attached a tablet pad with name {}", DEVICE->name); Debug::log(LOG, "Attached a tablet pad with name {}", DEVICE->name);

View file

@ -146,20 +146,6 @@ void CMonitor::onConnect(bool noRule) {
if (!noRule) if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
for (const auto& PTOUCHDEV : g_pInputManager->m_vTouches) {
if (matchesStaticSelector(PTOUCHDEV->boundOutput)) {
Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV->hlName, szName);
// wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, &PTOUCHDEV->wlr()->base, output);
}
}
for (const auto& PTABLET : g_pInputManager->m_lTablets) {
if (matchesStaticSelector(PTABLET.boundOutput)) {
Debug::log(LOG, "Binding tablet {} to output {}", PTABLET.name, szName);
// wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTABLET.wlrDevice, output);
}
}
if (!state.commit()) if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit"); Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");

View file

@ -85,72 +85,6 @@ struct SDrag {
DYNLISTENER(commitIcon); DYNLISTENER(commitIcon);
}; };
struct STablet {
DYNLISTENER(Tip);
DYNLISTENER(Axis);
DYNLISTENER(Button);
DYNLISTENER(Proximity);
DYNLISTENER(Destroy);
wlr_tablet* wlrTablet = nullptr;
wlr_tablet_v2_tablet* wlrTabletV2 = nullptr;
wlr_input_device* wlrDevice = nullptr;
bool relativeInput = false;
std::string name = "";
std::string boundOutput = "";
CBox activeArea;
//
bool operator==(const STablet& b) const {
return wlrDevice == b.wlrDevice;
}
};
struct STabletTool {
wlr_tablet_tool* wlrTabletTool = nullptr;
wlr_tablet_v2_tablet_tool* wlrTabletToolV2 = nullptr;
wlr_tablet_v2_tablet* wlrTabletOwnerV2 = nullptr;
wlr_surface* pSurface = nullptr;
double tiltX = 0;
double tiltY = 0;
bool active = true;
std::string name = "";
DYNLISTENER(TabletToolDestroy);
DYNLISTENER(TabletToolSetCursor);
bool operator==(const STabletTool& b) const {
return wlrTabletTool == b.wlrTabletTool;
}
};
struct STabletPad {
wlr_tablet_v2_tablet_pad* wlrTabletPadV2 = nullptr;
STablet* pTabletParent = nullptr;
wlr_input_device* pWlrDevice = nullptr;
std::string name = "";
DYNLISTENER(Attach);
DYNLISTENER(Button);
DYNLISTENER(Strip);
DYNLISTENER(Ring);
DYNLISTENER(Destroy);
bool operator==(const STabletPad& b) const {
return wlrTabletPadV2 == b.wlrTabletPadV2;
}
};
struct SSwipeGesture { struct SSwipeGesture {
PHLWORKSPACE pWorkspaceBegin = nullptr; PHLWORKSPACE pWorkspaceBegin = nullptr;

View file

@ -69,7 +69,6 @@ extern "C" {
#include <wlr/util/edges.h> #include <wlr/util/edges.h>
#include <wlr/types/wlr_tablet_pad.h> #include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h> #include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_tablet_v2.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/gles2.h> #include <wlr/render/gles2.h>

View file

@ -635,37 +635,60 @@ void CPointerManager::warpTo(const Vector2D& logical) {
void CPointerManager::move(const Vector2D& deltaLogical) { void CPointerManager::move(const Vector2D& deltaLogical) {
const auto oldPos = pointerPos; const auto oldPos = pointerPos;
auto newPos = oldPos + deltaLogical; auto newPos = oldPos + Vector2D{std::isnan(deltaLogical.x) ? 0.0 : deltaLogical.x, std::isnan(deltaLogical.y) ? 0.0 : deltaLogical.y};
warpTo(newPos); warpTo(newPos);
} }
void CPointerManager::warpAbsolute(const Vector2D& abs, SP<IHID> dev) { void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
SP<CMonitor> currentMonitor = g_pCompositor->m_pLastMonitor.lock(); SP<CMonitor> currentMonitor = g_pCompositor->m_pLastMonitor.lock();
if (!currentMonitor)
return;
if (!std::isnan(abs.x))
abs.x = std::clamp(abs.x, 0.0, 1.0);
if (!std::isnan(abs.y))
abs.y = std::clamp(abs.y, 0.0, 1.0);
// in logical global
CBox mappedArea = currentMonitor->logicalBox();
switch (dev->getType()) { switch (dev->getType()) {
case HID_TYPE_TABLET: case HID_TYPE_TABLET: {
//TODO: CTablet* TAB = reinterpret_cast<CTablet*>(dev.get());
break; if (!TAB->boundOutput.empty()) {
case HID_TYPE_TOUCH: { if (const auto PMONITOR = g_pCompositor->getMonitorFromString(TAB->boundOutput); PMONITOR) {
auto TOUCH = SP<ITouch>(dev);
if (!TOUCH->boundOutput.empty()) {
const auto PMONITOR = g_pCompositor->getMonitorFromString(TOUCH->boundOutput);
if (PMONITOR)
currentMonitor = PMONITOR->self.lock(); currentMonitor = PMONITOR->self.lock();
mappedArea = currentMonitor->logicalBox();
}
}
if (!TAB->boundBox.empty())
mappedArea = TAB->boundBox.translate(currentMonitor->vecPosition);
break;
}
case HID_TYPE_TOUCH: {
ITouch* TOUCH = reinterpret_cast<ITouch*>(dev.get());
if (!TOUCH->boundOutput.empty()) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromString(TOUCH->boundOutput); PMONITOR) {
currentMonitor = PMONITOR->self.lock();
mappedArea = currentMonitor->logicalBox();
}
} }
break; break;
} }
default: break; default: break;
} }
if (!currentMonitor)
return;
damageIfSoftware(); damageIfSoftware();
pointerPos = currentMonitor->vecPosition + currentMonitor->vecSize * abs; if (std::isnan(abs.x) || std::isnan(abs.y)) {
pointerPos.x = std::isnan(abs.x) ? pointerPos.x : mappedArea.x + mappedArea.w * abs.x;
pointerPos.y = std::isnan(abs.y) ? pointerPos.y : mappedArea.y + mappedArea.h * abs.y;
} else
pointerPos = mappedArea.pos() + mappedArea.size() * abs;
onCursorMoved(); onCursorMoved();
recheckEnteredOutputs(); recheckEnteredOutputs();
@ -839,6 +862,47 @@ void CPointerManager::attachTouch(SP<ITouch> touch) {
Debug::log(LOG, "Attached touch {} to global", touch->hlName); Debug::log(LOG, "Attached touch {} to global", touch->hlName);
} }
void CPointerManager::attachTablet(SP<CTablet> tablet) {
if (!tablet)
return;
auto listener = tabletListeners.emplace_back(makeShared<STabletListener>());
listener->tablet = tablet;
// clang-format off
listener->destroy = tablet->events.destroy.registerListener([this] (std::any d) {
detachTablet(nullptr);
});
listener->axis = tablet->tabletEvents.axis.registerListener([this] (std::any e) {
auto E = std::any_cast<CTablet::SAxisEvent>(e);
g_pInputManager->onTabletAxis(E);
});
listener->proximity = tablet->tabletEvents.proximity.registerListener([this] (std::any e) {
auto E = std::any_cast<CTablet::SProximityEvent>(e);
g_pInputManager->onTabletProximity(E);
});
listener->tip = tablet->tabletEvents.tip.registerListener([this] (std::any e) {
auto E = std::any_cast<CTablet::STipEvent>(e);
g_pInputManager->onTabletTip(E);
});
listener->button = tablet->tabletEvents.button.registerListener([this] (std::any e) {
auto E = std::any_cast<CTablet::SButtonEvent>(e);
g_pInputManager->onTabletButton(E);
});
// clang-format on
Debug::log(LOG, "Attached tablet {} to global", tablet->hlName);
}
void CPointerManager::detachPointer(SP<IPointer> pointer) { void CPointerManager::detachPointer(SP<IPointer> pointer) {
std::erase_if(pointerListeners, [pointer](const auto& e) { return e->pointer.expired() || e->pointer == pointer; }); std::erase_if(pointerListeners, [pointer](const auto& e) { return e->pointer.expired() || e->pointer == pointer; });
} }
@ -847,6 +911,10 @@ void CPointerManager::detachTouch(SP<ITouch> touch) {
std::erase_if(touchListeners, [touch](const auto& e) { return e->touch.expired() || e->touch == touch; }); std::erase_if(touchListeners, [touch](const auto& e) { return e->touch.expired() || e->touch == touch; });
} }
void CPointerManager::detachTablet(SP<CTablet> tablet) {
std::erase_if(tabletListeners, [tablet](const auto& e) { return e->tablet.expired() || e->tablet == tablet; });
}
void CPointerManager::damageCursor(SP<CMonitor> pMonitor) { void CPointerManager::damageCursor(SP<CMonitor> pMonitor) {
for (auto& mw : monitorStates) { for (auto& mw : monitorStates) {
if (mw->monitor != pMonitor) if (mw->monitor != pMonitor)

View file

@ -2,6 +2,7 @@
#include "../devices/IPointer.hpp" #include "../devices/IPointer.hpp"
#include "../devices/ITouch.hpp" #include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "../helpers/Box.hpp" #include "../helpers/Box.hpp"
#include "../helpers/Region.hpp" #include "../helpers/Region.hpp"
#include "../desktop/WLSurface.hpp" #include "../desktop/WLSurface.hpp"
@ -22,18 +23,18 @@ class CPointerManager {
public: public:
CPointerManager(); CPointerManager();
// pointers will move the cursor on their respective events
void attachPointer(SP<IPointer> pointer); void attachPointer(SP<IPointer> pointer);
// touch inputs won't move the cursor, it needs to be done manually
void attachTouch(SP<ITouch> touch); void attachTouch(SP<ITouch> touch);
void attachTablet(SP<CTablet> tablet);
void detachPointer(SP<IPointer> pointer); void detachPointer(SP<IPointer> pointer);
void detachTouch(SP<ITouch> touch); void detachTouch(SP<ITouch> touch);
void detachTablet(SP<CTablet> tablet);
// only clamps to the layout. // only clamps to the layout.
void warpTo(const Vector2D& logical); void warpTo(const Vector2D& logical);
void move(const Vector2D& deltaLogical); void move(const Vector2D& deltaLogical);
void warpAbsolute(const Vector2D& abs, SP<IHID> dev); void warpAbsolute(Vector2D abs, SP<IHID> dev);
void setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale); void setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale);
void setCursorSurface(CWLSurface* buf, const Vector2D& hotspot); void setCursorSurface(CWLSurface* buf, const Vector2D& hotspot);
@ -111,6 +112,17 @@ class CPointerManager {
}; };
std::vector<SP<STouchListener>> touchListeners; std::vector<SP<STouchListener>> touchListeners;
struct STabletListener {
CHyprSignalListener destroy;
CHyprSignalListener axis;
CHyprSignalListener proximity;
CHyprSignalListener tip;
CHyprSignalListener button;
WP<CTablet> tablet;
};
std::vector<SP<STabletListener>> tabletListeners;
struct { struct {
std::vector<CBox> monitorBoxes; std::vector<CBox> monitorBoxes;
} currentMonitorLayout; } currentMonitorLayout;

View file

@ -25,6 +25,7 @@
#include "../protocols/OutputManagement.hpp" #include "../protocols/OutputManagement.hpp"
#include "../protocols/ServerDecorationKDE.hpp" #include "../protocols/ServerDecorationKDE.hpp"
#include "../protocols/FocusGrab.hpp" #include "../protocols/FocusGrab.hpp"
#include "../protocols/Tablet.hpp"
CProtocolManager::CProtocolManager() { CProtocolManager::CProtocolManager() {
@ -53,6 +54,7 @@ CProtocolManager::CProtocolManager() {
PROTO::outputManagement = std::make_unique<COutputManagementProtocol>(&zwlr_output_manager_v1_interface, 4, "OutputManagement"); PROTO::outputManagement = std::make_unique<COutputManagementProtocol>(&zwlr_output_manager_v1_interface, 4, "OutputManagement");
PROTO::serverDecorationKDE = std::make_unique<CServerDecorationKDEProtocol>(&org_kde_kwin_server_decoration_manager_interface, 1, "ServerDecorationKDE"); PROTO::serverDecorationKDE = std::make_unique<CServerDecorationKDEProtocol>(&org_kde_kwin_server_decoration_manager_interface, 1, "ServerDecorationKDE");
PROTO::focusGrab = std::make_unique<CFocusGrabProtocol>(&hyprland_focus_grab_manager_v1_interface, 1, "FocusGrab"); PROTO::focusGrab = std::make_unique<CFocusGrabProtocol>(&hyprland_focus_grab_manager_v1_interface, 1, "FocusGrab");
PROTO::tablet = std::make_unique<CTabletV2Protocol>(&zwp_tablet_manager_v2_interface, 1, "TabletV2");
// Old protocol implementations. // Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner. // TODO: rewrite them to use hyprwayland-scanner.

View file

@ -58,9 +58,9 @@ CInputManager::~CInputManager() {
m_vKeyboards.clear(); m_vKeyboards.clear();
m_vPointers.clear(); m_vPointers.clear();
m_vTouches.clear(); m_vTouches.clear();
m_lTablets.clear(); m_vTablets.clear();
m_lTabletTools.clear(); m_vTabletTools.clear();
m_lTabletPads.clear(); m_vTabletPads.clear();
m_vIdleInhibitors.clear(); m_vIdleInhibitors.clear();
m_lSwitches.clear(); m_lSwitches.clear();
} }
@ -777,6 +777,8 @@ void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard)
} }
void CInputManager::setupKeyboard(SP<IKeyboard> keeb) { void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
m_vHIDs.push_back(keeb);
try { try {
keeb->hlName = getNameForNewDevice(keeb->wlr()->base.name); keeb->hlName = getNameForNewDevice(keeb->wlr()->base.name);
} catch (std::exception& e) { } catch (std::exception& e) {
@ -977,6 +979,8 @@ void CInputManager::newMouse(wlr_input_device* mouse) {
} }
void CInputManager::setupMouse(SP<IPointer> mauz) { void CInputManager::setupMouse(SP<IPointer> mauz) {
m_vHIDs.push_back(mauz);
try { try {
mauz->hlName = getNameForNewDevice(mauz->wlr()->base.name); mauz->hlName = getNameForNewDevice(mauz->wlr()->base.name);
} catch (std::exception& e) { } catch (std::exception& e) {
@ -1165,6 +1169,11 @@ void CInputManager::setPointerConfigs() {
} }
} }
static void removeFromHIDs(WP<IHID> hid) {
std::erase_if(g_pInputManager->m_vHIDs, [hid](const auto& e) { return e.expired() || e == hid; });
g_pInputManager->updateCapabilities();
}
void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) { void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
if (pKeyboard->xkbTranslationState) if (pKeyboard->xkbTranslationState)
xkb_state_unref(pKeyboard->xkbTranslationState); xkb_state_unref(pKeyboard->xkbTranslationState);
@ -1180,6 +1189,8 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
g_pCompositor->m_sSeat.keyboard.reset(); g_pCompositor->m_sSeat.keyboard.reset();
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr); wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr);
} }
removeFromHIDs(pKeyboard);
} }
void CInputManager::destroyPointer(SP<IPointer> mouse) { void CInputManager::destroyPointer(SP<IPointer> mouse) {
@ -1189,12 +1200,40 @@ void CInputManager::destroyPointer(SP<IPointer> mouse) {
if (!g_pCompositor->m_sSeat.mouse.expired()) if (!g_pCompositor->m_sSeat.mouse.expired())
unconstrainMouse(); unconstrainMouse();
removeFromHIDs(mouse);
} }
void CInputManager::destroyTouchDevice(SP<ITouch> touch) { void CInputManager::destroyTouchDevice(SP<ITouch> touch) {
Debug::log(LOG, "Touch device at {:x} removed", (uintptr_t)touch.get()); Debug::log(LOG, "Touch device at {:x} removed", (uintptr_t)touch.get());
std::erase_if(m_vTouches, [touch](const auto& other) { return other == touch; }); std::erase_if(m_vTouches, [touch](const auto& other) { return other == touch; });
removeFromHIDs(touch);
}
void CInputManager::destroyTablet(SP<CTablet> tablet) {
Debug::log(LOG, "Tablet device at {:x} removed", (uintptr_t)tablet.get());
std::erase_if(m_vTablets, [tablet](const auto& other) { return other == tablet; });
removeFromHIDs(tablet);
}
void CInputManager::destroyTabletTool(SP<CTabletTool> tool) {
Debug::log(LOG, "Tablet tool at {:x} removed", (uintptr_t)tool.get());
std::erase_if(m_vTabletTools, [tool](const auto& other) { return other == tool; });
removeFromHIDs(tool);
}
void CInputManager::destroyTabletPad(SP<CTabletPad> pad) {
Debug::log(LOG, "Tablet pad at {:x} removed", (uintptr_t)pad.get());
std::erase_if(m_vTabletPads, [pad](const auto& other) { return other == pad; });
removeFromHIDs(pad);
} }
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) { void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
@ -1338,14 +1377,19 @@ bool CInputManager::isConstrained() {
void CInputManager::updateCapabilities() { void CInputManager::updateCapabilities() {
uint32_t caps = 0; uint32_t caps = 0;
if (!m_vKeyboards.empty()) for (auto& h : m_vHIDs) {
if (h.expired())
continue;
auto cap = h->getCapabilities();
if (cap & HID_INPUT_CAPABILITY_KEYBOARD)
caps |= WL_SEAT_CAPABILITY_KEYBOARD; caps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (!m_vPointers.empty()) if (cap & HID_INPUT_CAPABILITY_POINTER)
caps |= WL_SEAT_CAPABILITY_POINTER; caps |= WL_SEAT_CAPABILITY_POINTER;
if (!m_vTouches.empty()) if (cap & HID_INPUT_CAPABILITY_TOUCH)
caps |= WL_SEAT_CAPABILITY_TOUCH; caps |= WL_SEAT_CAPABILITY_TOUCH;
if (!m_lTabletTools.empty()) }
caps |= WL_SEAT_CAPABILITY_POINTER;
wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps); wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps);
m_uiCapabilities = caps; m_uiCapabilities = caps;
@ -1380,6 +1424,7 @@ void CInputManager::disableAllKeyboards(bool virt) {
void CInputManager::newTouchDevice(wlr_input_device* pDevice) { void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(wlr_touch_from_input_device(pDevice))); const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(wlr_touch_from_input_device(pDevice)));
m_vHIDs.push_back(PNEWDEV);
try { try {
PNEWDEV->hlName = getNameForNewDevice(pDevice->name); PNEWDEV->hlName = getNameForNewDevice(pDevice->name);
@ -1450,43 +1495,39 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
} }
void CInputManager::setTabletConfigs() { void CInputManager::setTabletConfigs() {
for (auto& t : m_lTablets) { for (auto& t : m_vTablets) {
if (wlr_input_device_is_libinput(t.wlrDevice)) { if (wlr_input_device_is_libinput(&t->wlr()->base)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(t.wlrDevice); const auto NAME = t->hlName;
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&t->wlr()->base);
const auto RELINPUT = g_pConfigManager->getDeviceInt(t.name, "relative_input", "input:tablet:relative_input"); const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input");
t.relativeInput = RELINPUT; t->relativeInput = RELINPUT;
const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(t.name, "transform", "input:tablet:transform"), 0, 7); const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(NAME, "transform", "input:tablet:transform"), 0, 7);
Debug::log(LOG, "Setting calibration matrix for device {}", t.name); Debug::log(LOG, "Setting calibration matrix for device {}", NAME);
libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]); libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]);
if (g_pConfigManager->getDeviceInt(t.name, "left_handed", "input:tablet:left_handed") == 0) if (g_pConfigManager->getDeviceInt(NAME, "left_handed", "input:tablet:left_handed") == 0)
libinput_device_config_left_handed_set(LIBINPUTDEV, 0); libinput_device_config_left_handed_set(LIBINPUTDEV, 0);
else else
libinput_device_config_left_handed_set(LIBINPUTDEV, 1); libinput_device_config_left_handed_set(LIBINPUTDEV, 1);
const auto OUTPUT = g_pConfigManager->getDeviceString(t.name, "output", "input:tablet:output"); const auto OUTPUT = g_pConfigManager->getDeviceString(NAME, "output", "input:tablet:output");
const auto PMONITOR = g_pCompositor->getMonitorFromString(OUTPUT); if (OUTPUT != STRVAL_EMPTY) {
if (!OUTPUT.empty() && OUTPUT != STRVAL_EMPTY && PMONITOR) { Debug::log(LOG, "Binding tablet {} to output {}", NAME, OUTPUT);
Debug::log(LOG, "Binding tablet {} to output {}", t.name, PMONITOR->szName); t->boundOutput = OUTPUT;
// wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, t.wlrDevice, PMONITOR->output); } else
// wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, nullptr); t->boundOutput = "";
t.boundOutput = OUTPUT;
} else if (!PMONITOR)
Debug::log(ERR, "Failed to bind tablet {} to output '{}': monitor not found", t.name, OUTPUT);
const auto REGION_POS = g_pConfigManager->getDeviceVec(t.name, "region_position", "input:tablet:region_position"); const auto REGION_POS = g_pConfigManager->getDeviceVec(NAME, "region_position", "input:tablet:region_position");
const auto REGION_SIZE = g_pConfigManager->getDeviceVec(t.name, "region_size", "input:tablet:region_size"); const auto REGION_SIZE = g_pConfigManager->getDeviceVec(NAME, "region_size", "input:tablet:region_size");
//auto regionBox = CBox{REGION_POS.x, REGION_POS.y, REGION_SIZE.x, REGION_SIZE.y}; t->boundBox = {REGION_POS, REGION_SIZE};
// if (!regionBox.empty())
// wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, regionBox.pWlr());
const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(t.name, "active_area_size", "input:tablet:active_area_size"); const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(NAME, "active_area_size", "input:tablet:active_area_size");
const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(t.name, "active_area_position", "input:tablet:active_area_position"); const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(NAME, "active_area_position", "input:tablet:active_area_position");
if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) { if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) {
t.activeArea = CBox{ACTIVE_AREA_POS.x / t.wlrTablet->width_mm, ACTIVE_AREA_POS.y / t.wlrTablet->height_mm, t->activeArea = CBox{ACTIVE_AREA_POS.x / t->wlr()->width_mm, ACTIVE_AREA_POS.y / t->wlr()->height_mm, (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t->wlr()->width_mm,
(ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t.wlrTablet->width_mm, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t.wlrTablet->height_mm}; (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->wlr()->height_mm};
} }
} }
} }
@ -1575,16 +1616,16 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) {
[&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTouches.end()) [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTouches.end())
dupeno++; dupeno++;
while (std::find_if(m_lTabletPads.begin(), m_lTabletPads.end(), while (std::find_if(m_vTabletPads.begin(), m_vTabletPads.end(),
[&](const STabletPad& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletPads.end()) [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTabletPads.end())
dupeno++; dupeno++;
while (std::find_if(m_lTablets.begin(), m_lTablets.end(), while (std::find_if(m_vTablets.begin(), m_vTablets.end(),
[&](const STablet& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTablets.end()) [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTablets.end())
dupeno++; dupeno++;
while (std::find_if(m_lTabletTools.begin(), m_lTabletTools.end(), while (std::find_if(m_vTabletTools.begin(), m_vTabletTools.end(),
[&](const STabletTool& other) { return other.name == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_lTabletTools.end()) [&](const auto& other) { return other->hlName == proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }) != m_vTabletTools.end())
dupeno++; dupeno++;
return proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); return proposedNewName + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno)));

View file

@ -9,6 +9,7 @@
#include "../../helpers/signal/Listener.hpp" #include "../../helpers/signal/Listener.hpp"
#include "../../devices/IPointer.hpp" #include "../../devices/IPointer.hpp"
#include "../../devices/ITouch.hpp" #include "../../devices/ITouch.hpp"
#include "../../devices/Tablet.hpp"
class CPointerConstraint; class CPointerConstraint;
class CWindow; class CWindow;
@ -87,9 +88,15 @@ class CInputManager {
void newVirtualMouse(SP<CVirtualPointerV1Resource>); void newVirtualMouse(SP<CVirtualPointerV1Resource>);
void newTouchDevice(wlr_input_device*); void newTouchDevice(wlr_input_device*);
void newSwitch(wlr_input_device*); void newSwitch(wlr_input_device*);
void newTabletTool(wlr_tablet_tool*);
void newTabletPad(wlr_input_device*);
void newTablet(wlr_input_device*);
void destroyTouchDevice(SP<ITouch>); void destroyTouchDevice(SP<ITouch>);
void destroyKeyboard(SP<IKeyboard>); void destroyKeyboard(SP<IKeyboard>);
void destroyPointer(SP<IPointer>); void destroyPointer(SP<IPointer>);
void destroyTablet(SP<CTablet>);
void destroyTabletTool(SP<CTabletTool>);
void destroyTabletPad(SP<CTabletPad>);
void destroySwitch(SSwitchDevice*); void destroySwitch(SSwitchDevice*);
void unconstrainMouse(); void unconstrainMouse();
@ -116,6 +123,15 @@ class CInputManager {
void onTouchUp(ITouch::SUpEvent); void onTouchUp(ITouch::SUpEvent);
void onTouchMove(ITouch::SMotionEvent); void onTouchMove(ITouch::SMotionEvent);
void onSwipeBegin(IPointer::SSwipeBeginEvent);
void onSwipeEnd(IPointer::SSwipeEndEvent);
void onSwipeUpdate(IPointer::SSwipeUpdateEvent);
void onTabletAxis(CTablet::SAxisEvent);
void onTabletProximity(CTablet::SProximityEvent);
void onTabletTip(CTablet::STipEvent);
void onTabletButton(CTablet::SButtonEvent);
STouchData m_sTouchData; STouchData m_sTouchData;
// for dragging floating windows // for dragging floating windows
@ -131,11 +147,10 @@ class CInputManager {
std::vector<SP<IKeyboard>> m_vKeyboards; std::vector<SP<IKeyboard>> m_vKeyboards;
std::vector<SP<IPointer>> m_vPointers; std::vector<SP<IPointer>> m_vPointers;
std::vector<SP<ITouch>> m_vTouches; std::vector<SP<ITouch>> m_vTouches;
std::vector<SP<CTablet>> m_vTablets;
// tablets std::vector<SP<CTabletTool>> m_vTabletTools;
std::list<STablet> m_lTablets; std::vector<SP<CTabletPad>> m_vTabletPads;
std::list<STabletTool> m_lTabletTools; std::vector<WP<IHID>> m_vHIDs; // general container for all HID devices connected to the input manager.
std::list<STabletPad> m_lTabletPads;
// Switches // Switches
std::list<SSwitchDevice> m_lSwitches; std::list<SSwitchDevice> m_lSwitches;
@ -147,16 +162,9 @@ class CInputManager {
std::vector<WP<CPointerConstraint>> m_vConstraints; std::vector<WP<CPointerConstraint>> m_vConstraints;
// //
void newTabletTool(wlr_input_device*);
void newTabletPad(wlr_input_device*);
void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
void newIdleInhibitor(std::any); void newIdleInhibitor(std::any);
void recheckIdleInhibitorStatus(); void recheckIdleInhibitorStatus();
void onSwipeBegin(IPointer::SSwipeBeginEvent);
void onSwipeEnd(IPointer::SSwipeEndEvent);
void onSwipeUpdate(IPointer::SSwipeUpdateEvent);
SSwipeGesture m_sActiveSwipe; SSwipeGesture m_sActiveSwipe;
CTimer m_tmrLastCursorMovement; CTimer m_tmrLastCursorMovement;
@ -223,7 +231,7 @@ class CInputManager {
void mouseMoveUnified(uint32_t, bool refocus = false); void mouseMoveUnified(uint32_t, bool refocus = false);
STabletTool* ensureTabletToolPresent(wlr_tablet_tool*); SP<CTabletTool> ensureTabletToolPresent(wlr_tablet_tool*);
void applyConfigToKeyboard(SP<IKeyboard>); void applyConfigToKeyboard(SP<IKeyboard>);

View file

@ -1,290 +1,291 @@
#include "InputManager.hpp" #include "InputManager.hpp"
#include "../../Compositor.hpp" #include "../../Compositor.hpp"
#include "../../protocols/IdleNotify.hpp" #include "../../protocols/IdleNotify.hpp"
#include "../../protocols/Tablet.hpp"
#include "../../devices/Tablet.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../protocols/PointerConstraints.hpp"
void CInputManager::newTabletTool(wlr_input_device* pDevice) { static void unfocusTool(SP<CTabletTool> tool) {
const auto PNEWTABLET = &m_lTablets.emplace_back(); if (!tool->getSurface())
return;
tool->setSurface(nullptr);
if (tool->isDown)
PROTO::tablet->up(tool);
for (auto& b : tool->buttonsDown) {
PROTO::tablet->buttonTool(tool, b, false);
}
PROTO::tablet->proximityOut(tool);
}
static void focusTool(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf) {
if (tool->getSurface() == surf || !surf)
return;
if (tool->getSurface() && tool->getSurface() != surf)
unfocusTool(tool);
tool->setSurface(surf);
PROTO::tablet->proximityIn(tool, tablet, surf);
if (tool->isDown)
PROTO::tablet->down(tool);
for (auto& b : tool->buttonsDown) {
PROTO::tablet->buttonTool(tool, b, true);
}
}
static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = false) {
const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pInputManager->m_pLastMouseSurface);
if (!LASTHLSURFACE || !tool->active) {
if (tool->getSurface())
unfocusTool(tool);
return;
}
const auto BOX = LASTHLSURFACE->getSurfaceBoxGlobal();
if (!BOX.has_value()) {
if (tool->getSurface())
unfocusTool(tool);
return;
}
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
focusTool(tool, tab, g_pInputManager->m_pLastMouseSurface);
if (!motion)
return;
if (LASTHLSURFACE->constraint() && tool->wlr()->type != WLR_TABLET_TOOL_TYPE_MOUSE) {
// cursor logic will completely break here as the cursor will be locked.
// let's just "map" the desired position to the constraint area.
Vector2D local;
// yes, this technically ignores any regions set by the app. Too bad!
if (LASTHLSURFACE->getWindow())
local = tool->absolutePos * LASTHLSURFACE->getWindow()->m_vRealSize.goal();
else
local = tool->absolutePos * BOX->size();
if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11)
local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy;
PROTO::tablet->motion(tool, local);
return;
}
auto local = CURSORPOS - BOX->pos();
if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11)
local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy;
PROTO::tablet->motion(tool, local);
}
void CInputManager::onTabletAxis(CTablet::SAxisEvent e) {
const auto PTAB = e.tablet;
const auto PTOOL = ensureTabletToolPresent(e.tool);
if (PTOOL->active && (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y))) {
double x = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axis.x : NAN;
double dx = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axisDelta.x : NAN;
double y = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y) ? e.axis.y : NAN;
double dy = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y) ? e.axisDelta.y : NAN;
Vector2D delta = {std::isnan(dx) ? 0.0 : dx, std::isnan(dy) ? 0.0 : dy};
switch (e.tool->type) {
case WLR_TABLET_TOOL_TYPE_MOUSE: {
g_pPointerManager->move(delta);
break;
}
default: {
if (!std::isnan(x))
PTOOL->absolutePos.x = x;
if (!std::isnan(y))
PTOOL->absolutePos.y = y;
if (PTAB->relativeInput)
g_pPointerManager->move(delta);
else {
//Calculate transformations if active area is set
if (!PTAB->activeArea.empty()) {
if (!std::isnan(x))
x = (x - PTAB->activeArea.x) / (PTAB->activeArea.w - PTAB->activeArea.x);
if (!std::isnan(y))
y = (y - PTAB->activeArea.y) / (PTAB->activeArea.h - PTAB->activeArea.y);
}
g_pPointerManager->warpAbsolute({x, y}, PTAB);
}
break;
}
}
refocusTablet(PTAB, PTOOL, true);
m_tmrLastCursorMovement.reset();
}
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE)
PROTO::tablet->pressure(PTOOL, e.pressure);
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_DISTANCE)
PROTO::tablet->distance(PTOOL, e.distance);
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_ROTATION)
PROTO::tablet->rotation(PTOOL, e.rotation);
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_SLIDER)
PROTO::tablet->slider(PTOOL, e.slider);
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_WHEEL)
PROTO::tablet->wheel(PTOOL, e.wheelDelta);
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X)
PTOOL->tilt.x = e.tilt.x;
if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y)
PTOOL->tilt.y = e.tilt.y;
if (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y))
PROTO::tablet->tilt(PTOOL, PTOOL->tilt);
PROTO::idle->onActivity();
}
void CInputManager::onTabletTip(CTablet::STipEvent e) {
const auto PTAB = e.tablet;
const auto PTOOL = ensureTabletToolPresent(e.tool);
if (e.in) {
simulateMouseMovement();
refocusTablet(PTAB, PTOOL);
PROTO::tablet->down(PTOOL);
} else
PROTO::tablet->up(PTOOL);
PTOOL->isDown = e.in;
PROTO::idle->onActivity();
}
void CInputManager::onTabletButton(CTablet::SButtonEvent e) {
const auto PTOOL = ensureTabletToolPresent(e.tool);
PROTO::tablet->buttonTool(PTOOL, e.button, e.down);
if (e.down)
PTOOL->buttonsDown.push_back(e.button);
else
std::erase(PTOOL->buttonsDown, e.button);
PROTO::idle->onActivity();
}
void CInputManager::onTabletProximity(CTablet::SProximityEvent e) {
const auto PTAB = e.tablet;
const auto PTOOL = ensureTabletToolPresent(e.tool);
PTOOL->active = e.in;
if (!e.in) {
if (PTOOL->getSurface())
unfocusTool(PTOOL);
} else {
simulateMouseMovement();
refocusTablet(PTAB, PTOOL);
}
PROTO::idle->onActivity();
}
void CInputManager::newTablet(wlr_input_device* pDevice) {
const auto PNEWTABLET = m_vTablets.emplace_back(CTablet::create(wlr_tablet_from_input_device(pDevice)));
m_vHIDs.push_back(PNEWTABLET);
try { try {
PNEWTABLET->name = deviceNameToInternalString(pDevice->name); PNEWTABLET->hlName = deviceNameToInternalString(pDevice->name);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Tablet had no name???"); // logic error Debug::log(ERR, "Tablet had no name???"); // logic error
} }
PNEWTABLET->wlrTablet = wlr_tablet_from_input_device(pDevice); g_pPointerManager->attachTablet(PNEWTABLET);
PNEWTABLET->wlrDevice = pDevice;
PNEWTABLET->wlrTabletV2 = wlr_tablet_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice);
PNEWTABLET->wlrTablet->data = PNEWTABLET;
Debug::log(LOG, "Attaching tablet to cursor!"); PNEWTABLET->events.destroy.registerStaticListener(
[this](void* owner, std::any d) {
// wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, pDevice); auto TABLET = ((CTablet*)owner)->self;
destroyTablet(TABLET.lock());
PNEWTABLET->hyprListener_Destroy.initCallback(
&pDevice->events.destroy,
[](void* owner, void* data) {
const auto PTAB = (STablet*)owner;
g_pInputManager->m_lTablets.remove(*PTAB);
Debug::log(LOG, "Removed a tablet");
}, },
PNEWTABLET, "Tablet"); PNEWTABLET.get());
PNEWTABLET->hyprListener_Axis.initCallback(
&wlr_tablet_from_input_device(pDevice)->events.axis,
[](void* owner, void* data) {
const auto EVENT = (wlr_tablet_tool_axis_event*)data;
const auto PTAB = (STablet*)owner;
switch (EVENT->tool->type) {
case WLR_TABLET_TOOL_TYPE_MOUSE:
// wlr_cursor_move(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, EVENT->dx, EVENT->dy);
g_pInputManager->simulateMouseMovement();
g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
g_pInputManager->m_tmrLastCursorMovement.reset();
break;
default:
double x = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? EVENT->x : NAN;
double dx = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_X) ? EVENT->dx : NAN;
double y = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? EVENT->y : NAN;
double dy = (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_Y) ? EVENT->dy : NAN;
// if (PTAB->relativeInput)
// wlr_cursor_move(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, dx, dy);
// else {
// Calculate transformations if active area is set
// if (!PTAB->activeArea.empty()) {
// x = (x - PTAB->activeArea.x) / (PTAB->activeArea.w - PTAB->activeArea.x);
// y = (y - PTAB->activeArea.y) / (PTAB->activeArea.h - PTAB->activeArea.y);
// }
// wlr_cursor_warp_absolute(g_pCompositor->m_sWLRCursor, PTAB->wlrDevice, x, y);
// }
g_pInputManager->simulateMouseMovement();
g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
g_pInputManager->m_tmrLastCursorMovement.reset();
break;
}
const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
// TODO: this might be wrong
if (PTOOL->active) {
g_pInputManager->simulateMouseMovement();
g_pInputManager->focusTablet(PTAB, EVENT->tool, true);
}
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
wlr_tablet_v2_tablet_tool_notify_pressure(PTOOL->wlrTabletToolV2, EVENT->pressure);
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
wlr_tablet_v2_tablet_tool_notify_distance(PTOOL->wlrTabletToolV2, EVENT->distance);
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
wlr_tablet_v2_tablet_tool_notify_rotation(PTOOL->wlrTabletToolV2, EVENT->rotation);
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
wlr_tablet_v2_tablet_tool_notify_slider(PTOOL->wlrTabletToolV2, EVENT->slider);
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
wlr_tablet_v2_tablet_tool_notify_wheel(PTOOL->wlrTabletToolV2, EVENT->wheel_delta, 0);
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
PTOOL->tiltX = EVENT->tilt_x;
if (EVENT->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
PTOOL->tiltY = EVENT->tilt_y;
if (EVENT->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
wlr_tablet_v2_tablet_tool_notify_tilt(PTOOL->wlrTabletToolV2, PTOOL->tiltX, PTOOL->tiltY);
PROTO::idle->onActivity();
},
PNEWTABLET, "Tablet");
PNEWTABLET->hyprListener_Tip.initCallback(
&wlr_tablet_from_input_device(pDevice)->events.tip,
[](void* owner, void* data) {
const auto EVENT = (wlr_tablet_tool_tip_event*)data;
const auto PTAB = (STablet*)owner;
const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
// TODO: this might be wrong
if (EVENT->state == WLR_TABLET_TOOL_TIP_DOWN) {
g_pInputManager->simulateMouseMovement();
g_pInputManager->focusTablet(PTAB, EVENT->tool);
wlr_send_tablet_v2_tablet_tool_down(PTOOL->wlrTabletToolV2);
} else {
wlr_send_tablet_v2_tablet_tool_up(PTOOL->wlrTabletToolV2);
}
PROTO::idle->onActivity();
},
PNEWTABLET, "Tablet");
PNEWTABLET->hyprListener_Button.initCallback(
&wlr_tablet_from_input_device(pDevice)->events.button,
[](void* owner, void* data) {
const auto EVENT = (wlr_tablet_tool_button_event*)data;
const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
wlr_tablet_v2_tablet_tool_notify_button(PTOOL->wlrTabletToolV2, (zwp_tablet_pad_v2_button_state)EVENT->button, (zwp_tablet_pad_v2_button_state)EVENT->state);
PROTO::idle->onActivity();
},
PNEWTABLET, "Tablet");
PNEWTABLET->hyprListener_Proximity.initCallback(
&wlr_tablet_from_input_device(pDevice)->events.proximity,
[](void* owner, void* data) {
const auto EVENT = (wlr_tablet_tool_proximity_event*)data;
const auto PTAB = (STablet*)owner;
const auto PTOOL = g_pInputManager->ensureTabletToolPresent(EVENT->tool);
if (EVENT->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
PTOOL->active = false;
if (PTOOL->pSurface) {
wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
PTOOL->pSurface = nullptr;
}
} else {
PTOOL->active = true;
g_pInputManager->simulateMouseMovement();
g_pInputManager->focusTablet(PTAB, EVENT->tool);
}
PROTO::idle->onActivity();
},
PNEWTABLET, "Tablet");
setTabletConfigs(); setTabletConfigs();
} }
STabletTool* CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) { SP<CTabletTool> CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) {
if (pTool->data == nullptr) { if (pTool->data == nullptr) {
const auto PTOOL = &m_lTabletTools.emplace_back(); const auto PTOOL = m_vTabletTools.emplace_back(CTabletTool::create(pTool));
m_vHIDs.push_back(PTOOL);
Debug::log(LOG, "Creating tablet tool v2 for {:x}", (uintptr_t)pTool); PTOOL->events.destroy.registerStaticListener(
[this](void* owner, std::any d) {
PTOOL->wlrTabletTool = pTool; auto TOOL = ((CTabletTool*)owner)->self;
pTool->data = PTOOL; destroyTabletTool(TOOL.lock());
PTOOL->wlrTabletToolV2 = wlr_tablet_tool_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pTool);
PTOOL->hyprListener_TabletToolDestroy.initCallback(
&pTool->events.destroy,
[](void* owner, void* data) {
const auto PTOOL = (STabletTool*)owner;
PTOOL->wlrTabletTool->data = nullptr;
g_pInputManager->m_lTabletTools.remove(*PTOOL);
}, },
PTOOL, "Tablet Tool V1"); PTOOL.get());
//TODO: set cursor request
} }
return (STabletTool*)pTool->data; return CTabletTool::fromWlr(pTool);
} }
void CInputManager::newTabletPad(wlr_input_device* pDevice) { void CInputManager::newTabletPad(wlr_input_device* pDevice) {
const auto PNEWPAD = &m_lTabletPads.emplace_back(); const auto PNEWPAD = m_vTabletPads.emplace_back(CTabletPad::create(wlr_tablet_pad_from_input_device(pDevice)));
m_vHIDs.push_back(PNEWPAD);
try { try {
PNEWPAD->name = deviceNameToInternalString(pDevice->name); PNEWPAD->hlName = deviceNameToInternalString(pDevice->name);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Pad had no name???"); // logic error Debug::log(ERR, "Pad had no name???"); // logic error
} }
PNEWPAD->wlrTabletPadV2 = wlr_tablet_pad_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice); // clang-format off
PNEWPAD->pWlrDevice = pDevice; PNEWPAD->events.destroy.registerStaticListener([this](void* owner, std::any d) {
auto PAD = ((CTabletPad*)owner)->self;
destroyTabletPad(PAD.lock());
}, PNEWPAD.get());
PNEWPAD->hyprListener_Button.initCallback( PNEWPAD->padEvents.button.registerStaticListener([this](void* owner, std::any e) {
&wlr_tablet_pad_from_input_device(pDevice)->events.button, const auto E = std::any_cast<CTabletPad::SButtonEvent>(e);
[](void* owner, void* data) { const auto PPAD = ((CTabletPad*)owner)->self.lock();
const auto EVENT = (wlr_tablet_pad_button_event*)data;
const auto PPAD = (STabletPad*)owner;
wlr_tablet_v2_tablet_pad_notify_mode(PPAD->wlrTabletPadV2, EVENT->group, EVENT->mode, EVENT->time_msec); PROTO::tablet->mode(PPAD, 0, E.mode, E.timeMs);
wlr_tablet_v2_tablet_pad_notify_button(PPAD->wlrTabletPadV2, EVENT->button, EVENT->time_msec, (zwp_tablet_pad_v2_button_state)EVENT->state); PROTO::tablet->buttonPad(PPAD, E.button, E.timeMs, E.down);
}, }, PNEWPAD.get());
PNEWPAD, "Tablet Pad");
PNEWPAD->hyprListener_Strip.initCallback( PNEWPAD->padEvents.strip.registerStaticListener([this](void* owner, std::any e) {
&wlr_tablet_pad_from_input_device(pDevice)->events.strip, const auto E = std::any_cast<CTabletPad::SStripEvent>(e);
[](void* owner, void* data) { const auto PPAD = ((CTabletPad*)owner)->self.lock();
const auto EVENT = (wlr_tablet_pad_strip_event*)data;
const auto PPAD = (STabletPad*)owner;
wlr_tablet_v2_tablet_pad_notify_strip(PPAD->wlrTabletPadV2, EVENT->strip, EVENT->position, EVENT->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER, EVENT->time_msec); PROTO::tablet->strip(PPAD, E.strip, E.position, E.finger, E.timeMs);
}, }, PNEWPAD.get());
PNEWPAD, "Tablet Pad");
PNEWPAD->hyprListener_Ring.initCallback( PNEWPAD->padEvents.ring.registerStaticListener([this](void* owner, std::any e) {
&wlr_tablet_pad_from_input_device(pDevice)->events.strip, const auto E = std::any_cast<CTabletPad::SRingEvent>(e);
[](void* owner, void* data) { const auto PPAD = ((CTabletPad*)owner)->self.lock();
const auto EVENT = (wlr_tablet_pad_ring_event*)data;
const auto PPAD = (STabletPad*)owner;
wlr_tablet_v2_tablet_pad_notify_ring(PPAD->wlrTabletPadV2, EVENT->ring, EVENT->position, EVENT->source == WLR_TABLET_PAD_RING_SOURCE_FINGER, EVENT->time_msec); PROTO::tablet->ring(PPAD, E.ring, E.position, E.finger, E.timeMs);
}, }, PNEWPAD.get());
PNEWPAD, "Tablet Pad");
PNEWPAD->hyprListener_Attach.initCallback( PNEWPAD->padEvents.attach.registerStaticListener([this](void* owner, std::any e) {
&wlr_tablet_pad_from_input_device(pDevice)->events.strip, const auto PPAD = ((CTabletPad*)owner)->self.lock();
[](void* owner, void* data) { const auto TOOL = std::any_cast<SP<CTabletTool>>(e);
const auto TABLET = (wlr_tablet_tool*)data;
const auto PPAD = (STabletPad*)owner;
PPAD->pTabletParent = (STablet*)TABLET->data; PPAD->parent = TOOL;
}, PNEWPAD.get());
if (!PPAD->pTabletParent) // clang-format on
Debug::log(ERR, "tabletpad got attached to a nullptr tablet!! this might be bad.");
},
PNEWPAD, "Tablet Pad");
PNEWPAD->hyprListener_Destroy.initCallback(
&pDevice->events.destroy,
[](void* owner, void* data) {
const auto PPAD = (STabletPad*)owner;
g_pInputManager->m_lTabletPads.remove(*PPAD);
Debug::log(LOG, "Removed a tablet pad");
},
PNEWPAD, "Tablet Pad");
}
void CInputManager::focusTablet(STablet* pTab, wlr_tablet_tool* pTool, bool motion) {
const auto PTOOL = g_pInputManager->ensureTabletToolPresent(pTool);
if (const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); PWINDOW) {
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
if (PTOOL->pSurface != g_pInputManager->m_pLastMouseSurface)
wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
if (g_pInputManager->m_pLastMouseSurface) {
PTOOL->pSurface = g_pCompositor->m_pLastFocus;
wlr_tablet_v2_tablet_tool_notify_proximity_in(PTOOL->wlrTabletToolV2, pTab->wlrTabletV2, g_pInputManager->m_pLastMouseSurface);
}
if (motion) {
auto local = CURSORPOS - PWINDOW->m_vRealPosition.goal();
if (PWINDOW->m_bIsX11)
local = local * PWINDOW->m_fX11SurfaceScaledBy;
wlr_tablet_v2_tablet_tool_notify_motion(PTOOL->wlrTabletToolV2, local.x, local.y);
}
} else {
if (PTOOL->pSurface)
wlr_tablet_v2_tablet_tool_notify_proximity_out(PTOOL->wlrTabletToolV2);
}
} }

656
src/protocols/Tablet.cpp Normal file
View file

@ -0,0 +1,656 @@
#include "Tablet.hpp"
#include "../devices/Tablet.hpp"
#include "../Compositor.hpp"
#include <algorithm>
#define LOGM PROTO::tablet->protoLog
CTabletPadStripV2Resource::CTabletPadStripV2Resource(SP<CZwpTabletPadStripV2> resource_, uint32_t id_) : id(id_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletPadStripV2Resource::good() {
return resource->resource();
}
CTabletPadRingV2Resource::CTabletPadRingV2Resource(SP<CZwpTabletPadRingV2> resource_, uint32_t id_) : id(id_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletPadRingV2Resource::good() {
return resource->resource();
}
CTabletPadGroupV2Resource::CTabletPadGroupV2Resource(SP<CZwpTabletPadGroupV2> resource_, size_t idx_) : idx(idx_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletPadGroupV2Resource::good() {
return resource->resource();
}
void CTabletPadGroupV2Resource::sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group) {
resource->sendModes(group->mode_count);
wl_array buttonArr;
wl_array_init(&buttonArr);
wl_array_add(&buttonArr, group->button_count * sizeof(int));
memcpy(buttonArr.data, group->buttons, group->button_count * sizeof(int));
resource->sendButtons(&buttonArr);
wl_array_release(&buttonArr);
for (size_t i = 0; i < group->strip_count; ++i) {
const auto RESOURCE =
PROTO::tablet->m_vStrips.emplace_back(makeShared<CTabletPadStripV2Resource>(makeShared<CZwpTabletPadStripV2>(resource->client(), resource->version(), 0), i));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vStrips.pop_back();
return;
}
resource->sendStrip(RESOURCE->resource.get());
}
for (size_t i = 0; i < group->ring_count; ++i) {
const auto RESOURCE =
PROTO::tablet->m_vRings.emplace_back(makeShared<CTabletPadRingV2Resource>(makeShared<CZwpTabletPadRingV2>(resource->client(), resource->version(), 0), i));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vRings.pop_back();
return;
}
resource->sendRing(RESOURCE->resource.get());
}
resource->sendDone();
}
CTabletPadV2Resource::CTabletPadV2Resource(SP<CZwpTabletPadV2> resource_, SP<CTabletPad> pad_, SP<CTabletSeat> seat_) : pad(pad_), seat(seat_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletPadV2Resource::good() {
return resource->resource();
}
void CTabletPadV2Resource::sendData() {
// this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
const char** path_ptr;
for (path_ptr = (const char**)(&pad->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&pad->wlr()->paths)->data + (&pad->wlr()->paths)->size); (path_ptr)++) {
resource->sendPath(*path_ptr);
}
resource->sendButtons(pad->wlr()->button_count);
wlr_tablet_pad_group* group;
size_t i = 0;
wl_list_for_each(group, &pad->wlr()->groups, link) {
createGroup(group, i++);
}
resource->sendDone();
}
void CTabletPadV2Resource::createGroup(wlr_tablet_pad_group* group, size_t idx) {
const auto RESOURCE =
PROTO::tablet->m_vGroups.emplace_back(makeShared<CTabletPadGroupV2Resource>(makeShared<CZwpTabletPadGroupV2>(resource->client(), resource->version(), 0), idx));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vGroups.pop_back();
return;
}
resource->sendGroup(RESOURCE->resource.get());
RESOURCE->sendData(pad.lock(), group);
}
CTabletV2Resource::CTabletV2Resource(SP<CZwpTabletV2> resource_, SP<CTablet> tablet_, SP<CTabletSeat> seat_) : tablet(tablet_), seat(seat_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletV2Resource::good() {
return resource->resource();
}
void CTabletV2Resource::sendData() {
resource->sendName(tablet->deviceName.c_str());
resource->sendId(tablet->wlr()->usb_vendor_id, tablet->wlr()->usb_product_id);
// this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
const char** path_ptr;
for (path_ptr = (const char**)(&tablet->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&tablet->wlr()->paths)->data + (&tablet->wlr()->paths)->size);
(path_ptr)++) {
resource->sendPath(*path_ptr);
}
resource->sendDone();
}
CTabletToolV2Resource::CTabletToolV2Resource(SP<CZwpTabletToolV2> resource_, SP<CTabletTool> tool_, SP<CTabletSeat> seat_) : tool(tool_), seat(seat_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); });
resource->setSetCursor([this](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) {
wlr_seat_pointer_request_set_cursor_event e;
e.hotspot_x = hot_x;
e.hotspot_y = hot_y;
e.surface = surf ? wlr_surface_from_resource(surf) : nullptr;
e.serial = serial;
g_pInputManager->processMouseRequest(&e);
});
}
CTabletToolV2Resource::~CTabletToolV2Resource() {
if (frameSource)
wl_event_source_remove(frameSource);
}
bool CTabletToolV2Resource::good() {
return resource->resource();
}
void CTabletToolV2Resource::sendData() {
static auto WLR_TYPE_TO_PROTO = [](uint32_t wlr) -> zwpTabletToolV2Type {
switch (wlr) {
case WLR_TABLET_TOOL_TYPE_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN;
case WLR_TABLET_TOOL_TYPE_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER;
case WLR_TABLET_TOOL_TYPE_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH;
case WLR_TABLET_TOOL_TYPE_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL;
case WLR_TABLET_TOOL_TYPE_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH;
case WLR_TABLET_TOOL_TYPE_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE;
case WLR_TABLET_TOOL_TYPE_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS;
default: ASSERT(false);
}
UNREACHABLE();
};
resource->sendType(WLR_TYPE_TO_PROTO(tool->wlr()->type));
resource->sendHardwareSerial(tool->wlr()->hardware_serial >> 32, tool->wlr()->hardware_serial & 0xFFFFFFFF);
resource->sendHardwareIdWacom(tool->wlr()->hardware_wacom >> 32, tool->wlr()->hardware_wacom & 0xFFFFFFFF);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_DISTANCE)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_PRESSURE)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_ROTATION)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_SLIDER)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_TILT)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_TILT);
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_WHEEL)
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL);
resource->sendDone();
}
void CTabletToolV2Resource::queueFrame() {
if (frameSource)
return;
frameSource = wl_event_loop_add_idle(
g_pCompositor->m_sWLEventLoop, [](void* data) { ((CTabletToolV2Resource*)data)->sendFrame(false); }, this);
}
void CTabletToolV2Resource::sendFrame(bool removeSource) {
if (frameSource) {
if (removeSource)
wl_event_source_remove(frameSource);
frameSource = nullptr;
}
if (!current)
return;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
resource->sendFrame(now.tv_sec * 1000 + now.tv_nsec / 1000000);
}
CTabletSeat::CTabletSeat(SP<CZwpTabletSeatV2> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); });
resource->setOnDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); });
}
bool CTabletSeat::good() {
return resource->resource();
}
void CTabletSeat::sendTool(SP<CTabletTool> tool) {
const auto RESOURCE =
PROTO::tablet->m_vTools.emplace_back(makeShared<CTabletToolV2Resource>(makeShared<CZwpTabletToolV2>(resource->client(), resource->version(), 0), tool, self.lock()));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vTools.pop_back();
return;
}
resource->sendToolAdded(RESOURCE->resource.get());
RESOURCE->sendData();
tools.push_back(RESOURCE);
}
void CTabletSeat::sendPad(SP<CTabletPad> pad) {
const auto RESOURCE =
PROTO::tablet->m_vPads.emplace_back(makeShared<CTabletPadV2Resource>(makeShared<CZwpTabletPadV2>(resource->client(), resource->version(), 0), pad, self.lock()));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vPads.pop_back();
return;
}
resource->sendPadAdded(RESOURCE->resource.get());
RESOURCE->sendData();
pads.push_back(RESOURCE);
}
void CTabletSeat::sendTablet(SP<CTablet> tablet) {
const auto RESOURCE =
PROTO::tablet->m_vTablets.emplace_back(makeShared<CTabletV2Resource>(makeShared<CZwpTabletV2>(resource->client(), resource->version(), 0), tablet, self.lock()));
if (!RESOURCE->good()) {
resource->noMemory();
PROTO::tablet->m_vTablets.pop_back();
return;
}
resource->sendTabletAdded(RESOURCE->resource.get());
RESOURCE->sendData();
tablets.push_back(RESOURCE);
}
void CTabletSeat::sendData() {
for (auto& tw : PROTO::tablet->tablets) {
if (tw.expired())
continue;
sendTablet(tw.lock());
}
for (auto& tw : PROTO::tablet->tools) {
if (tw.expired())
continue;
sendTool(tw.lock());
}
for (auto& tw : PROTO::tablet->pads) {
if (tw.expired())
continue;
sendPad(tw.lock());
}
}
CTabletV2Protocol::CTabletV2Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CTabletV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CZwpTabletManagerV2>(client, ver, id)).get();
RESOURCE->setOnDestroy([this](CZwpTabletManagerV2* p) { this->onManagerResourceDestroy(p->resource()); });
RESOURCE->setDestroy([this](CZwpTabletManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
RESOURCE->setGetTabletSeat([this](CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat) { this->onGetSeat(pMgr, id, seat); });
}
void CTabletV2Protocol::onManagerResourceDestroy(wl_resource* res) {
std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
}
void CTabletV2Protocol::destroyResource(CTabletSeat* resource) {
std::erase_if(m_vSeats, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletToolV2Resource* resource) {
std::erase_if(m_vTools, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletV2Resource* resource) {
std::erase_if(m_vTablets, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletPadV2Resource* resource) {
std::erase_if(m_vPads, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletPadGroupV2Resource* resource) {
std::erase_if(m_vGroups, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletPadRingV2Resource* resource) {
std::erase_if(m_vRings, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::destroyResource(CTabletPadStripV2Resource* resource) {
std::erase_if(m_vStrips, [&](const auto& other) { return other.get() == resource; });
}
void CTabletV2Protocol::onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat) {
const auto RESOURCE = m_vSeats.emplace_back(makeShared<CTabletSeat>(makeShared<CZwpTabletSeatV2>(pMgr->client(), pMgr->version(), id)));
if (!RESOURCE->good()) {
pMgr->noMemory();
m_vSeats.pop_back();
return;
}
RESOURCE->self = RESOURCE;
RESOURCE->sendData();
}
void CTabletV2Protocol::registerDevice(SP<CTablet> tablet) {
for (auto& s : m_vSeats) {
s->sendTablet(tablet);
}
tablets.push_back(tablet);
}
void CTabletV2Protocol::registerDevice(SP<CTabletTool> tool) {
for (auto& s : m_vSeats) {
s->sendTool(tool);
}
tools.push_back(tool);
}
void CTabletV2Protocol::registerDevice(SP<CTabletPad> pad) {
for (auto& s : m_vSeats) {
s->sendPad(pad);
}
pads.push_back(pad);
}
void CTabletV2Protocol::unregisterDevice(SP<CTablet> tablet) {
for (auto& t : m_vTablets) {
if (t->tablet == tablet) {
t->resource->sendRemoved();
t->inert = true;
}
}
std::erase_if(tablets, [tablet](const auto& e) { return e.expired() || e == tablet; });
}
void CTabletV2Protocol::unregisterDevice(SP<CTabletTool> tool) {
for (auto& t : m_vTools) {
if (t->tool == tool) {
t->resource->sendRemoved();
t->inert = true;
}
}
std::erase_if(tools, [tool](const auto& e) { return e.expired() || e == tool; });
}
void CTabletV2Protocol::unregisterDevice(SP<CTabletPad> pad) {
for (auto& t : m_vPads) {
if (t->pad == pad) {
t->resource->sendRemoved();
t->inert = true;
}
}
std::erase_if(pads, [pad](const auto& e) { return e.expired() || e == pad; });
}
void CTabletV2Protocol::recheckRegisteredDevices() {
std::erase_if(tablets, [](const auto& e) { return e.expired(); });
std::erase_if(tools, [](const auto& e) { return e.expired(); });
std::erase_if(pads, [](const auto& e) { return e.expired(); });
// now we need to send removed events
for (auto& t : m_vTablets) {
if (!t->tablet.expired() || t->inert)
continue;
t->resource->sendRemoved();
t->inert = true;
}
for (auto& t : m_vTools) {
if (!t->tool.expired() || t->inert)
continue;
if (t->current) {
t->resource->sendProximityOut();
t->sendFrame();
t->lastSurf = nullptr;
}
t->resource->sendRemoved();
t->inert = true;
}
for (auto& t : m_vPads) {
if (!t->pad.expired() || t->inert)
continue;
t->resource->sendRemoved();
t->inert = true;
}
}
void CTabletV2Protocol::pressure(SP<CTabletTool> tool, double value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendPressure(std::clamp(value * 65535, 0.0, 65535.0));
t->queueFrame();
}
}
void CTabletV2Protocol::distance(SP<CTabletTool> tool, double value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendDistance(std::clamp(value * 65535, 0.0, 65535.0));
t->queueFrame();
}
}
void CTabletV2Protocol::rotation(SP<CTabletTool> tool, double value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendRotation(wl_fixed_from_double(value));
t->queueFrame();
}
}
void CTabletV2Protocol::slider(SP<CTabletTool> tool, double value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendSlider(std::clamp(value * 65535, -65535.0, 65535.0));
t->queueFrame();
}
}
void CTabletV2Protocol::wheel(SP<CTabletTool> tool, double value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendWheel(wl_fixed_from_double(value), 0);
t->queueFrame();
}
}
void CTabletV2Protocol::tilt(SP<CTabletTool> tool, const Vector2D& value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendTilt(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y));
t->queueFrame();
}
}
void CTabletV2Protocol::up(SP<CTabletTool> tool) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendUp();
t->queueFrame();
}
}
void CTabletV2Protocol::down(SP<CTabletTool> tool) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
t->resource->sendDown(serial);
t->queueFrame();
}
}
void CTabletV2Protocol::proximityIn(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf) {
proximityOut(tool);
const auto CLIENT = wl_resource_get_client(surf->resource);
SP<CTabletToolV2Resource> toolResource;
SP<CTabletV2Resource> tabletResource;
for (auto& t : m_vTools) {
if (t->tool != tool || t->resource->client() != CLIENT)
continue;
if (t->seat.expired()) {
LOGM(ERR, "proximityIn on a tool without a seat parent");
return;
}
if (t->lastSurf == surf)
return;
toolResource = t;
for (auto& tab : m_vTablets) {
if (tab->tablet != tablet)
continue;
if (tab->seat != t->seat || !tab->seat)
continue;
tabletResource = tab;
break;
}
}
if (!tabletResource || !toolResource) {
LOGM(ERR, "proximityIn on a tool and tablet without valid resource(s)??");
return;
}
toolResource->current = true;
toolResource->lastSurf = surf;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, toolResource->resource->client()));
toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->resource);
toolResource->queueFrame();
LOGM(ERR, "proximityIn: found no resource to send enter");
}
void CTabletV2Protocol::proximityOut(SP<CTabletTool> tool) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->current = false;
t->lastSurf = nullptr;
t->resource->sendProximityOut();
t->sendFrame();
}
}
void CTabletV2Protocol::buttonTool(SP<CTabletTool> tool, uint32_t button, uint32_t state) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
t->resource->sendButton(serial, button, (zwpTabletToolV2ButtonState)state);
t->queueFrame();
}
}
void CTabletV2Protocol::motion(SP<CTabletTool> tool, const Vector2D& value) {
for (auto& t : m_vTools) {
if (t->tool != tool || !t->current)
continue;
t->resource->sendMotion(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y));
t->queueFrame();
}
}
void CTabletV2Protocol::mode(SP<CTabletPad> pad, uint32_t group, uint32_t mode, uint32_t timeMs) {
for (auto& t : m_vPads) {
if (t->pad != pad)
continue;
if (t->groups.size() <= group) {
LOGM(ERR, "BUG THIS: group >= t->groups.size()");
return;
}
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
t->groups.at(group)->resource->sendModeSwitch(timeMs, serial, mode);
}
}
void CTabletV2Protocol::buttonPad(SP<CTabletPad> pad, uint32_t button, uint32_t timeMs, uint32_t state) {
for (auto& t : m_vPads) {
if (t->pad != pad)
continue;
t->resource->sendButton(timeMs, button, zwpTabletToolV2ButtonState{state});
}
}
void CTabletV2Protocol::strip(SP<CTabletPad> pad, uint32_t strip, double position, bool finger, uint32_t timeMs) {
LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::strip not implemented");
}
void CTabletV2Protocol::ring(SP<CTabletPad> pad, uint32_t ring, double position, bool finger, uint32_t timeMs) {
LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::ring not implemented");
}

236
src/protocols/Tablet.hpp Normal file
View file

@ -0,0 +1,236 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "tablet-v2.hpp"
#include "../helpers/Vector2D.hpp"
class CTablet;
class CTabletTool;
class CTabletPad;
class CEventLoopTimer;
class CTabletSeat;
class CTabletPadStripV2Resource {
public:
CTabletPadStripV2Resource(SP<CZwpTabletPadStripV2> resource_, uint32_t id);
bool good();
uint32_t id = 0;
private:
SP<CZwpTabletPadStripV2> resource;
friend class CTabletSeat;
friend class CTabletPadGroupV2Resource;
friend class CTabletV2Protocol;
};
class CTabletPadRingV2Resource {
public:
CTabletPadRingV2Resource(SP<CZwpTabletPadRingV2> resource_, uint32_t id);
bool good();
uint32_t id = 0;
private:
SP<CZwpTabletPadRingV2> resource;
friend class CTabletSeat;
friend class CTabletPadGroupV2Resource;
friend class CTabletV2Protocol;
};
class CTabletPadGroupV2Resource {
public:
CTabletPadGroupV2Resource(SP<CZwpTabletPadGroupV2> resource_, size_t idx);
bool good();
void sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group);
std::vector<WP<CTabletPadRingV2Resource>> rings;
std::vector<WP<CTabletPadStripV2Resource>> strips;
size_t idx = 0;
private:
SP<CZwpTabletPadGroupV2> resource;
friend class CTabletSeat;
friend class CTabletPadV2Resource;
friend class CTabletV2Protocol;
};
class CTabletPadV2Resource {
public:
CTabletPadV2Resource(SP<CZwpTabletPadV2> resource_, SP<CTabletPad> pad_, SP<CTabletSeat> seat_);
bool good();
void sendData();
std::vector<WP<CTabletPadGroupV2Resource>> groups;
WP<CTabletPad> pad;
WP<CTabletSeat> seat;
bool inert = false; // removed was sent
private:
SP<CZwpTabletPadV2> resource;
void createGroup(wlr_tablet_pad_group* group, size_t idx);
friend class CTabletSeat;
friend class CTabletV2Protocol;
};
class CTabletV2Resource {
public:
CTabletV2Resource(SP<CZwpTabletV2> resource_, SP<CTablet> tablet_, SP<CTabletSeat> seat_);
bool good();
void sendData();
WP<CTablet> tablet;
WP<CTabletSeat> seat;
bool inert = false; // removed was sent
private:
SP<CZwpTabletV2> resource;
friend class CTabletSeat;
friend class CTabletV2Protocol;
};
class CTabletToolV2Resource {
public:
CTabletToolV2Resource(SP<CZwpTabletToolV2> resource_, SP<CTabletTool> tool_, SP<CTabletSeat> seat_);
~CTabletToolV2Resource();
bool good();
void sendData();
void queueFrame();
void sendFrame(bool removeSource = true);
bool current = false;
wlr_surface* lastSurf = nullptr; // READ-ONLY
WP<CTabletTool> tool;
WP<CTabletSeat> seat;
wl_event_source* frameSource = nullptr;
bool inert = false; // removed was sent
private:
SP<CZwpTabletToolV2> resource;
friend class CTabletSeat;
friend class CTabletV2Protocol;
};
class CTabletSeat {
public:
CTabletSeat(SP<CZwpTabletSeatV2> resource_);
bool good();
void sendData();
std::vector<WP<CTabletToolV2Resource>> tools;
std::vector<WP<CTabletPadV2Resource>> pads;
std::vector<WP<CTabletV2Resource>> tablets;
void sendTool(SP<CTabletTool> tool);
void sendPad(SP<CTabletPad> pad);
void sendTablet(SP<CTablet> tablet);
private:
SP<CZwpTabletSeatV2> resource;
WP<CTabletSeat> self;
friend class CTabletV2Protocol;
};
class CTabletV2Protocol : public IWaylandProtocol {
public:
CTabletV2Protocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void registerDevice(SP<CTablet> tablet);
void registerDevice(SP<CTabletTool> tool);
void registerDevice(SP<CTabletPad> pad);
void unregisterDevice(SP<CTablet> tablet);
void unregisterDevice(SP<CTabletTool> tool);
void unregisterDevice(SP<CTabletPad> pad);
void recheckRegisteredDevices();
// Tablet tool events
void pressure(SP<CTabletTool> tool, double value);
void distance(SP<CTabletTool> tool, double value);
void rotation(SP<CTabletTool> tool, double value);
void slider(SP<CTabletTool> tool, double value);
void wheel(SP<CTabletTool> tool, double value);
void tilt(SP<CTabletTool> tool, const Vector2D& value);
void up(SP<CTabletTool> tool);
void down(SP<CTabletTool> tool);
void proximityIn(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* surf);
void proximityOut(SP<CTabletTool> tool);
void buttonTool(SP<CTabletTool> tool, uint32_t button, uint32_t state);
void motion(SP<CTabletTool> tool, const Vector2D& value);
// Tablet pad events
void mode(SP<CTabletPad> pad, uint32_t group, uint32_t mode, uint32_t timeMs);
void buttonPad(SP<CTabletPad> pad, uint32_t button, uint32_t timeMs, uint32_t state);
void strip(SP<CTabletPad> pad, uint32_t strip, double position, bool finger, uint32_t timeMs);
void ring(SP<CTabletPad> pad, uint32_t ring, double position, bool finger, uint32_t timeMs);
private:
void onManagerResourceDestroy(wl_resource* res);
void destroyResource(CTabletSeat* resource);
void destroyResource(CTabletToolV2Resource* resource);
void destroyResource(CTabletV2Resource* resource);
void destroyResource(CTabletPadV2Resource* resource);
void destroyResource(CTabletPadGroupV2Resource* resource);
void destroyResource(CTabletPadRingV2Resource* resource);
void destroyResource(CTabletPadStripV2Resource* resource);
void onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat);
//
std::vector<UP<CZwpTabletManagerV2>> m_vManagers;
std::vector<SP<CTabletSeat>> m_vSeats;
std::vector<SP<CTabletToolV2Resource>> m_vTools;
std::vector<SP<CTabletV2Resource>> m_vTablets;
std::vector<SP<CTabletPadV2Resource>> m_vPads;
std::vector<SP<CTabletPadGroupV2Resource>> m_vGroups;
std::vector<SP<CTabletPadRingV2Resource>> m_vRings;
std::vector<SP<CTabletPadStripV2Resource>> m_vStrips;
// registered
std::vector<WP<CTablet>> tablets;
std::vector<WP<CTabletTool>> tools;
std::vector<WP<CTabletPad>> pads;
// FIXME: rings and strips are broken, I don't understand how this shit works.
// It's 2am.
SP<CTabletPadRingV2Resource> ringForID(SP<CTabletPad> pad, uint32_t id);
SP<CTabletPadStripV2Resource> stripForID(SP<CTabletPad> pad, uint32_t id);
friend class CTabletSeat;
friend class CTabletToolV2Resource;
friend class CTabletV2Resource;
friend class CTabletPadV2Resource;
friend class CTabletPadGroupV2Resource;
friend class CTabletPadRingV2Resource;
friend class CTabletPadStripV2Resource;
};
namespace PROTO {
inline UP<CTabletV2Protocol> tablet;
};