aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/xwayland/Dnd.cpp
diff options
context:
space:
mode:
authorVaxry <[email protected]>2024-12-15 00:37:17 +0100
committerGitHub <[email protected]>2024-12-15 00:37:17 +0100
commitdb249648776875ce3142141d0d3055e43ce606aa (patch)
tree50cac68a73e0da6e8154bd4deef08a11e2784ad2 /src/xwayland/Dnd.cpp
parent9f7a96b997d90c4c188f3837e02859a25a05611e (diff)
downloadHyprland-db249648776875ce3142141d0d3055e43ce606aa.tar.gz
Hyprland-db249648776875ce3142141d0d3055e43ce606aa.zip
xwayland: Support cross DnD from Wayland (#8708)
Adds support for drag-and-drop from Wayland clients to XWayland ones
Diffstat (limited to 'src/xwayland/Dnd.cpp')
-rw-r--r--src/xwayland/Dnd.cpp211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/xwayland/Dnd.cpp b/src/xwayland/Dnd.cpp
new file mode 100644
index 00000000..488ee8dd
--- /dev/null
+++ b/src/xwayland/Dnd.cpp
@@ -0,0 +1,211 @@
+#include "Dnd.hpp"
+#include "XWM.hpp"
+#include "XWayland.hpp"
+#include "Server.hpp"
+#include "../managers/XWaylandManager.hpp"
+#include "../desktop/WLSurface.hpp"
+
+static xcb_atom_t dndActionToAtom(uint32_t actions) {
+ if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ return HYPRATOMS["XdndActionCopy"];
+ else if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ return HYPRATOMS["XdndActionMove"];
+ else if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+ return HYPRATOMS["XdndActionAsk"];
+
+ return XCB_ATOM_NONE;
+}
+
+eDataSourceType CX11DataOffer::type() {
+ return DATA_SOURCE_TYPE_X11;
+}
+
+SP<CWLDataOfferResource> CX11DataOffer::getWayland() {
+ return nullptr;
+}
+
+SP<CX11DataOffer> CX11DataOffer::getX11() {
+ return self.lock();
+}
+
+SP<IDataSource> CX11DataOffer::getSource() {
+ return source.lock();
+}
+
+void CX11DataOffer::markDead() {
+#ifndef NO_XWAYLAND
+ std::erase(g_pXWayland->pWM->dndDataOffers, self);
+#endif
+}
+
+void CX11DataDevice::sendDataOffer(SP<IDataOffer> offer) {
+ ; // no-op, I don't think this has an X equiv
+}
+
+void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<IDataOffer> offer) {
+#ifndef NO_XWAYLAND
+ auto XSURF = g_pXWayland->pWM->windowForWayland(surf);
+
+ if (offer == lastOffer)
+ return;
+
+ if (!XSURF) {
+ Debug::log(ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination");
+ return;
+ }
+
+ auto SOURCE = offer->getSource();
+
+ if (!SOURCE) {
+ Debug::log(ERR, "CX11DataDevice::sendEnter: No source");
+ return;
+ }
+
+ xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME);
+
+ xcb_client_message_data_t data = {0};
+ data.data32[0] = g_pXWayland->pWM->dndSelection.window;
+ data.data32[1] = XDND_VERSION << 24;
+
+ // let the client know it needs to check for DND_TYPE_LIST
+ data.data32[1] |= 1;
+
+ std::vector<xcb_atom_t> targets;
+
+ for (auto& mime : SOURCE->mimes()) {
+ targets.emplace_back(g_pXWayland->pWM->mimeToAtom(mime));
+ }
+
+ xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndTypeList"], XCB_ATOM_ATOM, 32, targets.size(),
+ targets.data());
+
+ g_pXWayland->pWM->sendDndEvent(surf, HYPRATOMS["XdndEnter"], data);
+
+ lastSurface = XSURF;
+ lastOffer = offer;
+
+ auto hlSurface = CWLSurface::fromResource(surf);
+ if (!hlSurface) {
+ Debug::log(ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!");
+ lastSurfaceCoords = {};
+ return;
+ }
+
+ lastSurfaceCoords = hlSurface->getSurfaceBoxGlobal().value_or(CBox{}).pos();
+#endif
+}
+
+void CX11DataDevice::sendLeave() {
+#ifndef NO_XWAYLAND
+ if (!lastSurface)
+ return;
+
+ xcb_client_message_data_t data = {0};
+ data.data32[0] = g_pXWayland->pWM->dndSelection.window;
+
+ g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndLeave"], data);
+
+ lastSurface.reset();
+ lastOffer.reset();
+
+ xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, XCB_ATOM_NONE, XCB_TIME_CURRENT_TIME);
+#endif
+}
+
+void CX11DataDevice::sendMotion(uint32_t timeMs, const Vector2D& local) {
+#ifndef NO_XWAYLAND
+ if (!lastSurface || !lastOffer || !lastOffer->getSource())
+ return;
+
+ const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local);
+
+ xcb_client_message_data_t data = {0};
+ data.data32[0] = g_pXWayland->pWM->dndSelection.window;
+ data.data32[2] = (((int32_t)XCOORDS.x) << 16) | (int32_t)XCOORDS.y;
+ data.data32[3] = timeMs;
+ data.data32[4] = dndActionToAtom(lastOffer->getSource()->actions());
+
+ g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndPosition"], data);
+ lastTime = timeMs;
+#endif
+}
+
+void CX11DataDevice::sendDrop() {
+#ifndef NO_XWAYLAND
+ if (!lastSurface || !lastOffer)
+ return;
+
+ // we don't have timeMs here, just send last time + 1
+ xcb_client_message_data_t data = {0};
+ data.data32[0] = g_pXWayland->pWM->dndSelection.window;
+ data.data32[2] = lastTime + 1;
+
+ g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndDrop"], data);
+
+ sendLeave();
+#endif
+}
+
+void CX11DataDevice::sendSelection(SP<IDataOffer> offer) {
+ ; // no-op. Selection is done separately.
+}
+
+eDataSourceType CX11DataDevice::type() {
+ return DATA_SOURCE_TYPE_X11;
+}
+
+SP<CWLDataDeviceResource> CX11DataDevice::getWayland() {
+ return nullptr;
+}
+
+SP<CX11DataDevice> CX11DataDevice::getX11() {
+ return self.lock();
+}
+
+std::vector<std::string> CX11DataSource::mimes() {
+ return mimeTypes;
+}
+
+void CX11DataSource::send(const std::string& mime, uint32_t fd) {
+ ;
+}
+
+void CX11DataSource::accepted(const std::string& mime) {
+ ;
+}
+
+void CX11DataSource::cancelled() {
+ ;
+}
+
+bool CX11DataSource::hasDnd() {
+ return dnd;
+}
+
+bool CX11DataSource::dndDone() {
+ return dropped;
+}
+
+void CX11DataSource::error(uint32_t code, const std::string& msg) {
+ Debug::log(ERR, "CX11DataSource::error: this fn is a stub: code {} msg {}", code, msg);
+}
+
+void CX11DataSource::sendDndFinished() {
+ ;
+}
+
+uint32_t CX11DataSource::actions() {
+ return supportedActions;
+}
+
+eDataSourceType CX11DataSource::type() {
+ return DATA_SOURCE_TYPE_X11;
+}
+
+void CX11DataSource::sendDndDropPerformed() {
+ ;
+}
+
+void CX11DataSource::sendDndAction(wl_data_device_manager_dnd_action a) {
+ ;
+}