From 84e8d1810d57b65a0bc3b5ac2998bc023df880f4 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Mon, 6 May 2024 02:15:26 +0100 Subject: Tablet: move to new impl Ring and strip are not implemented. Will I implement this? God fucking knows. Nobody seems to have that anyways. --- src/protocols/Tablet.cpp | 656 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 656 insertions(+) create mode 100644 src/protocols/Tablet.cpp (limited to 'src/protocols/Tablet.cpp') 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 + +#define LOGM PROTO::tablet->protoLog + +CTabletPadStripV2Resource::CTabletPadStripV2Resource(SP 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 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 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 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(makeShared(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(makeShared(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 resource_, SP pad_, SP 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(makeShared(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 resource_, SP tablet_, SP 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 resource_, SP tool_, SP 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 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 tool) { + const auto RESOURCE = + PROTO::tablet->m_vTools.emplace_back(makeShared(makeShared(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 pad) { + const auto RESOURCE = + PROTO::tablet->m_vPads.emplace_back(makeShared(makeShared(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 tablet) { + const auto RESOURCE = + PROTO::tablet->m_vTablets.emplace_back(makeShared(makeShared(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(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(makeShared(pMgr->client(), pMgr->version(), id))); + + if (!RESOURCE->good()) { + pMgr->noMemory(); + m_vSeats.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + RESOURCE->sendData(); +} + +void CTabletV2Protocol::registerDevice(SP tablet) { + for (auto& s : m_vSeats) { + s->sendTablet(tablet); + } + + tablets.push_back(tablet); +} + +void CTabletV2Protocol::registerDevice(SP tool) { + for (auto& s : m_vSeats) { + s->sendTool(tool); + } + + tools.push_back(tool); +} + +void CTabletV2Protocol::registerDevice(SP pad) { + for (auto& s : m_vSeats) { + s->sendPad(pad); + } + + pads.push_back(pad); +} + +void CTabletV2Protocol::unregisterDevice(SP 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 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 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 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 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 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 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 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 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 tool) { + for (auto& t : m_vTools) { + if (t->tool != tool || !t->current) + continue; + + t->resource->sendUp(); + t->queueFrame(); + } +} + +void CTabletV2Protocol::down(SP 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 tool, SP tablet, wlr_surface* surf) { + proximityOut(tool); + const auto CLIENT = wl_resource_get_client(surf->resource); + + SP toolResource; + SP 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 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 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 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 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 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 pad, uint32_t strip, double position, bool finger, uint32_t timeMs) { + LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::strip not implemented"); +} + +void CTabletV2Protocol::ring(SP pad, uint32_t ring, double position, bool finger, uint32_t timeMs) { + LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::ring not implemented"); +} -- cgit v1.2.3