summaryrefslogtreecommitdiff
path: root/src/protocols
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2024-05-06 02:15:26 +0100
committerVaxry <43317083+vaxerski@users.noreply.github.com>2024-05-09 13:37:39 +0100
commit84e8d1810d57b65a0bc3b5ac2998bc023df880f4 (patch)
treecd313a14333c06765dc149c9339d595c13eafd72 /src/protocols
parented411f53bdcf8d455f9e6ca769693aa84dc8372f (diff)
Tablet: move to new impl
Ring and strip are not implemented. Will I implement this? God fucking knows. Nobody seems to have that anyways.
Diffstat (limited to 'src/protocols')
-rw-r--r--src/protocols/Tablet.cpp656
-rw-r--r--src/protocols/Tablet.hpp236
2 files changed, 892 insertions, 0 deletions
diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp
new file mode 100644
index 00000000..b3ca76f5
--- /dev/null
+++ b/src/protocols/Tablet.cpp
@@ -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");
+}
diff --git a/src/protocols/Tablet.hpp b/src/protocols/Tablet.hpp
new file mode 100644
index 00000000..74a45c63
--- /dev/null
+++ b/src/protocols/Tablet.hpp
@@ -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;
+};