2024-08-30 14:53:25 +00:00
|
|
|
/*
|
2024-09-17 19:00:25 +00:00
|
|
|
* Deskflow -- mouse and keyboard sharing utility
|
2026-05-19 19:01:38 +00:00
|
|
|
* SPDX-FileCopyrightText: (C) 2024 - 2026 Deskflow Developers
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
* SPDX-FileCopyrightText: (C) 2024, 2026 Synergy App Ltd
|
|
|
|
|
* SPDX-FileCopyrightText: (C) 2022, 2026 Red Hat, Inc.
|
2025-01-24 00:11:17 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
2024-08-30 14:53:25 +00:00
|
|
|
*/
|
|
|
|
|
|
2024-10-21 20:39:15 +00:00
|
|
|
#include "platform/PortalInputCapture.h"
|
2025-07-13 14:26:11 +00:00
|
|
|
#include "base/DirectionTypes.h"
|
2024-08-30 14:53:25 +00:00
|
|
|
#include "base/Event.h"
|
|
|
|
|
#include "base/Log.h"
|
|
|
|
|
#include "base/TMethodJob.h"
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#include "deskflow/ClipboardTypes.h"
|
|
|
|
|
#include "platform/EiClipboard.h"
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-05-19 19:01:38 +00:00
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
|
|
|
|
#include "platform/PortalClipboard.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-02-02 01:10:24 +00:00
|
|
|
#ifdef HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE
|
|
|
|
|
#include "common/Settings.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-05-13 00:19:18 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <limits>
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#include <poll.h>
|
2024-08-30 14:53:25 +00:00
|
|
|
#include <sys/socket.h> // for EIS fd hack, remove
|
|
|
|
|
#include <sys/un.h> // for EIS fd hack, remove
|
|
|
|
|
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#include <QBuffer>
|
|
|
|
|
#include <QByteArray>
|
|
|
|
|
#include <QByteArrayList>
|
|
|
|
|
#include <QDataStream>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QImage>
|
|
|
|
|
#include <QtEndian>
|
|
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
namespace deskflow {
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-05-13 00:18:18 +00:00
|
|
|
const char *PortalInputCapture::barrierSideName(BarrierSide side)
|
|
|
|
|
{
|
|
|
|
|
using enum BarrierSide;
|
|
|
|
|
|
|
|
|
|
switch (side) {
|
|
|
|
|
case Left:
|
|
|
|
|
return "left";
|
|
|
|
|
case Right:
|
|
|
|
|
return "right";
|
|
|
|
|
case Top:
|
|
|
|
|
return "top";
|
|
|
|
|
case Bottom:
|
|
|
|
|
return "bottom";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 00:19:18 +00:00
|
|
|
int PortalInputCapture::scaleCoordinateBetweenRanges(
|
|
|
|
|
double value, int sourceMin, int sourceMax, int destinationMin, int destinationMax
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (sourceMax <= sourceMin || destinationMax <= destinationMin) {
|
|
|
|
|
return destinationMin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto clamped = std::clamp(value, static_cast<double>(sourceMin), static_cast<double>(sourceMax));
|
|
|
|
|
const auto fraction = (clamped - sourceMin) / (sourceMax - sourceMin);
|
|
|
|
|
const auto mapped = static_cast<int>(std::lround(destinationMin + fraction * (destinationMax - destinationMin)));
|
|
|
|
|
return std::clamp(mapped, destinationMin, destinationMax);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PortalInputCapture::getPortalBounds(Bounds &bounds) const
|
|
|
|
|
{
|
|
|
|
|
if (m_barrierInfo.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bounds.left = std::numeric_limits<gint>::max();
|
|
|
|
|
bounds.top = std::numeric_limits<gint>::max();
|
|
|
|
|
bounds.right = std::numeric_limits<gint>::min();
|
|
|
|
|
bounds.bottom = std::numeric_limits<gint>::min();
|
|
|
|
|
|
|
|
|
|
for (const auto &info : m_barrierInfo) {
|
|
|
|
|
bounds.left = std::min(bounds.left, info.x);
|
|
|
|
|
bounds.top = std::min(bounds.top, info.y);
|
|
|
|
|
bounds.right = std::max(bounds.right, info.x + static_cast<gint>(info.width) - 1);
|
|
|
|
|
bounds.bottom = std::max(bounds.bottom, info.y + static_cast<gint>(info.height) - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PortalInputCapture::getClosestReleaseBarrier(
|
|
|
|
|
double x, double y, int screenLeft, int screenTop, int screenRight, int screenBottom, const Bounds &portalBounds,
|
|
|
|
|
BarrierInfo &barrier
|
|
|
|
|
) const
|
|
|
|
|
{
|
|
|
|
|
const auto activeSides = m_screen->activeSides();
|
|
|
|
|
using enum DirectionMask;
|
|
|
|
|
|
|
|
|
|
auto side = BarrierSide::Left;
|
|
|
|
|
auto sideDistance = std::numeric_limits<double>::max();
|
|
|
|
|
const auto considerSide = [&side, &sideDistance](BarrierSide candidateSide, double candidateDistance) {
|
|
|
|
|
if (candidateDistance < sideDistance) {
|
|
|
|
|
side = candidateSide;
|
|
|
|
|
sideDistance = candidateDistance;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (activeSides & static_cast<int>(LeftMask)) {
|
|
|
|
|
considerSide(BarrierSide::Left, std::abs(x - screenLeft));
|
|
|
|
|
}
|
|
|
|
|
if (activeSides & static_cast<int>(RightMask)) {
|
|
|
|
|
considerSide(BarrierSide::Right, std::abs(x - screenRight));
|
|
|
|
|
}
|
|
|
|
|
if (activeSides & static_cast<int>(TopMask)) {
|
|
|
|
|
considerSide(BarrierSide::Top, std::abs(y - screenTop));
|
|
|
|
|
}
|
|
|
|
|
if (activeSides & static_cast<int>(BottomMask)) {
|
|
|
|
|
considerSide(BarrierSide::Bottom, std::abs(y - screenBottom));
|
|
|
|
|
}
|
|
|
|
|
if (sideDistance == std::numeric_limits<double>::max()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto portalX = scaleCoordinateBetweenRanges(x, screenLeft, screenRight, portalBounds.left, portalBounds.right);
|
|
|
|
|
const auto portalY = scaleCoordinateBetweenRanges(y, screenTop, screenBottom, portalBounds.top, portalBounds.bottom);
|
|
|
|
|
|
|
|
|
|
auto bestDistance = std::numeric_limits<int>::max();
|
|
|
|
|
for (const auto &info : m_barrierInfo) {
|
|
|
|
|
if (info.side != side) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto left = info.x;
|
|
|
|
|
const auto top = info.y;
|
|
|
|
|
const auto right = info.x + static_cast<gint>(info.width) - 1;
|
|
|
|
|
const auto bottom = info.y + static_cast<gint>(info.height) - 1;
|
|
|
|
|
auto distance = 0;
|
|
|
|
|
|
|
|
|
|
switch (side) {
|
|
|
|
|
case BarrierSide::Left:
|
|
|
|
|
case BarrierSide::Right:
|
|
|
|
|
if (portalY < top) {
|
|
|
|
|
distance = top - portalY;
|
|
|
|
|
} else if (portalY > bottom) {
|
|
|
|
|
distance = portalY - bottom;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BarrierSide::Top:
|
|
|
|
|
case BarrierSide::Bottom:
|
|
|
|
|
if (portalX < left) {
|
|
|
|
|
distance = left - portalX;
|
|
|
|
|
} else if (portalX > right) {
|
|
|
|
|
distance = portalX - right;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (distance < bestDistance) {
|
|
|
|
|
bestDistance = distance;
|
|
|
|
|
barrier = info;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bestDistance != std::numeric_limits<int>::max();
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-30 14:53:25 +00:00
|
|
|
PortalInputCapture::PortalInputCapture(EiScreen *screen, IEventQueue *events)
|
2025-06-20 00:49:53 +00:00
|
|
|
: m_screen{screen},
|
|
|
|
|
m_events{events},
|
2026-02-02 01:10:24 +00:00
|
|
|
m_portalVersion(0),
|
2025-06-20 15:16:55 +00:00
|
|
|
m_portal{xdp_portal_new()}
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
// Create clipboard for primary clipboard ID
|
|
|
|
|
m_clipboard = new EiClipboard(kClipboardClipboard);
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
m_glibMainLoop = g_main_loop_new(nullptr, true);
|
2025-07-03 13:05:45 +00:00
|
|
|
|
|
|
|
|
auto tMethodJob = new TMethodJob<PortalInputCapture>(this, &PortalInputCapture::glibThread);
|
|
|
|
|
m_glibThread = new Thread(tMethodJob);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 15:20:37 +00:00
|
|
|
auto captureCallback = [](gpointer data) { return static_cast<PortalInputCapture *>(data)->initSession(); };
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 15:20:37 +00:00
|
|
|
g_idle_add(captureCallback, this);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
PortalInputCapture::~PortalInputCapture()
|
|
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
if (g_main_loop_is_running(m_glibMainLoop))
|
|
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
if (m_glibThread) {
|
|
|
|
|
m_glibThread->cancel();
|
|
|
|
|
m_glibThread->wait();
|
|
|
|
|
m_glibThread = nullptr;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
g_main_loop_unref(m_glibMainLoop);
|
|
|
|
|
m_glibMainLoop = nullptr;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
if (m_session) {
|
2025-06-20 15:16:55 +00:00
|
|
|
using enum Signal;
|
|
|
|
|
XdpSession *parentSession = xdp_input_capture_session_get_session(m_session);
|
|
|
|
|
g_signal_handler_disconnect(G_OBJECT(parentSession), m_signals.at(SessionClosed));
|
|
|
|
|
g_signal_handler_disconnect(m_session, m_signals.at(Disabled));
|
|
|
|
|
g_signal_handler_disconnect(m_session, m_signals.at(Activated));
|
|
|
|
|
g_signal_handler_disconnect(m_session, m_signals.at(Deactivated));
|
|
|
|
|
g_signal_handler_disconnect(m_session, m_signals.at(ZonesChanged));
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
g_signal_handler_disconnect(m_session, m_signals.at(SelectionTransfer));
|
2025-06-20 00:49:53 +00:00
|
|
|
g_object_unref(m_session);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
for (auto b : m_barriers) {
|
2024-08-30 14:53:25 +00:00
|
|
|
g_object_unref(b);
|
|
|
|
|
}
|
2025-06-20 00:49:53 +00:00
|
|
|
m_barriers.clear();
|
|
|
|
|
g_object_unref(m_portal);
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
|
|
|
|
|
delete m_clipboard;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EiClipboard *PortalInputCapture::getClipboard(ClipboardID id) const
|
|
|
|
|
{
|
|
|
|
|
// Currently only supporting primary clipboard
|
|
|
|
|
if (id == kClipboardClipboard) {
|
|
|
|
|
return m_clipboard;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
gboolean PortalInputCapture::timeoutHandler() const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
return true; // keep re-triggering
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
void PortalInputCapture::handleSessionClosed(XdpSession *session)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_ERR("portal input capture session was closed, exiting");
|
2025-06-20 00:49:53 +00:00
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
2025-07-28 12:20:29 +00:00
|
|
|
m_events->addEvent(Event(EventTypes::Quit));
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 15:16:55 +00:00
|
|
|
g_signal_handler_disconnect(session, m_signals.at(Signal::SessionClosed));
|
|
|
|
|
m_signals.at(Signal::SessionClosed) = 0;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-18 23:36:37 +00:00
|
|
|
void PortalInputCapture::claimClipboardOwnership([[maybe_unused]] XdpSession *session)
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
{
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
2026-05-19 19:01:38 +00:00
|
|
|
PortalClipboard::claimOwnership(m_clipboard, session);
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 23:32:13 +00:00
|
|
|
void PortalInputCapture::readClipboardSelection(XdpSession *session) const
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
{
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
2026-05-19 19:01:38 +00:00
|
|
|
const qint64 maxBytes = static_cast<qint64>(m_screen->maximumClipboardSize()) * 1024;
|
|
|
|
|
LOG_DEBUG("clipboard read cap: %lld bytes", static_cast<long long>(maxBytes));
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
|
|
|
|
|
const char **mimeTypes = xdp_session_get_selection_mime_types(session);
|
|
|
|
|
if (!mimeTypes) {
|
|
|
|
|
LOG_DEBUG("clipboard has no mime types available to read");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 19:01:38 +00:00
|
|
|
if (PortalClipboard::readSelectionIntoCache(m_clipboard, session, mimeTypes, maxBytes))
|
|
|
|
|
m_screen->sendClipboardEvent(EventTypes::ClipboardGrabbed, kClipboardClipboard);
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#else
|
|
|
|
|
(void)session;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PortalInputCapture::handleSelectionTransfer(XdpSession *session, const char *mimeType, uint32_t serial)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
2026-05-19 17:28:02 +00:00
|
|
|
if (m_isActive) {
|
|
|
|
|
LOG_DEBUG("skipping clipboard selection transfer, clipboard is active");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-05-19 19:01:38 +00:00
|
|
|
PortalClipboard::serveSelectionTransfer(m_clipboard, session, mimeType, serial);
|
|
|
|
|
#else
|
|
|
|
|
(void)session;
|
|
|
|
|
(void)mimeType;
|
|
|
|
|
(void)serial;
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-02 01:10:24 +00:00
|
|
|
void PortalInputCapture::setupSession(XdpInputCaptureSession *session)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
g_autoptr(GError) error = nullptr;
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
XdpSession *parentSession = xdp_input_capture_session_get_session(session);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
2026-05-19 19:01:38 +00:00
|
|
|
if (!xdp_session_is_clipboard_enabled(parentSession) && m_portalVersion > 1) {
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
// Restored sessions can pre-date clipboard support, leaving the channel
|
|
|
|
|
// disabled even though we requested it. Drop the saved token and recreate
|
|
|
|
|
// the session from scratch so the user gets a fresh permission dialog.
|
|
|
|
|
LOG_WARN("clipboard not enabled on session, discarding restore token to force a fresh session");
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE
|
|
|
|
|
Settings::setValue(Settings::Server::XdpRestoreToken, QString());
|
|
|
|
|
#endif
|
|
|
|
|
g_object_unref(m_session);
|
|
|
|
|
m_session = nullptr;
|
|
|
|
|
g_idle_add([](gpointer data) { return static_cast<PortalInputCapture *>(data)->initSession(); }, this);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
auto fd = xdp_input_capture_session_connect_to_eis(session, &error);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
LOG_ERR("failed to connect to eis: %s", error->message);
|
2025-07-13 13:40:36 +00:00
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
2025-07-28 12:20:29 +00:00
|
|
|
m_events->addEvent(Event(EventTypes::Quit));
|
2025-07-13 13:40:36 +00:00
|
|
|
return;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-07-13 13:40:36 +00:00
|
|
|
|
2024-08-30 14:53:25 +00:00
|
|
|
// Socket ownership is transferred to the EiScreen
|
2025-06-20 00:49:53 +00:00
|
|
|
m_events->addEvent(Event(EventTypes::EIConnected, m_screen->getEventTarget(), EiScreen::EiConnectInfo::alloc(fd)));
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 15:16:55 +00:00
|
|
|
using enum Signal;
|
|
|
|
|
m_signals.at(Disabled) = g_signal_connect(G_OBJECT(session), "disabled", G_CALLBACK(disabled), this);
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
m_signals.at(Activated) = g_signal_connect(G_OBJECT(session), "activated", G_CALLBACK(activated), this);
|
|
|
|
|
m_signals.at(Deactivated) = g_signal_connect(G_OBJECT(session), "deactivated", G_CALLBACK(deactivated), this);
|
|
|
|
|
m_signals.at(ZonesChanged) = g_signal_connect(G_OBJECT(session), "zones-changed", G_CALLBACK(zonesChanged), this);
|
2025-06-20 15:16:55 +00:00
|
|
|
m_signals.at(SessionClosed) = g_signal_connect(G_OBJECT(parentSession), "closed", G_CALLBACK(sessionClosed), this);
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
handleZonesChanged(session, nullptr);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
|
|
|
|
m_signals.at(SelectionTransfer) =
|
|
|
|
|
g_signal_connect(G_OBJECT(parentSession), "selection-transfer", G_CALLBACK(selectionTransfer), this);
|
|
|
|
|
#endif
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-02 01:10:24 +00:00
|
|
|
void PortalInputCapture::handleInitSession(GObject *object, GAsyncResult *res)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("portal input capture session initialized");
|
|
|
|
|
|
|
|
|
|
auto session = xdp_portal_create_input_capture_session_finish(XDP_PORTAL(object), res, &error);
|
|
|
|
|
if (!session) {
|
|
|
|
|
LOG_ERR("failed to initialize input capture session, quitting: %s", error->message);
|
|
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
|
|
|
|
m_events->addEvent(Event(EventTypes::Quit));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_session = session;
|
|
|
|
|
|
|
|
|
|
setupSession(session);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PortalInputCapture::handleStart(GObject *object, GAsyncResult *res)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE
|
|
|
|
|
LOG_DEBUG("portal input capture session initialized");
|
|
|
|
|
if (!xdp_input_capture_session_start_finish(m_session, res, &error)) {
|
|
|
|
|
LOG_ERR("failed to start input capture session, quitting: %s", error->message);
|
|
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
|
|
|
|
m_events->addEvent(Event(EventTypes::Quit));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 03:27:58 +00:00
|
|
|
auto restoreToken = QString(xdp_input_capture_session_get_restore_token(m_session));
|
|
|
|
|
if (!restoreToken.isEmpty()) {
|
|
|
|
|
Settings::setValue(Settings::Server::XdpRestoreToken, restoreToken);
|
2026-02-02 01:10:24 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
setupSession(m_session);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 02:29:34 +00:00
|
|
|
void PortalInputCapture::handleSetPointerBarriers(const GObject *, GAsyncResult *res)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
auto failed_list = xdp_input_capture_session_set_pointer_barriers_finish(m_session, res, &error);
|
2024-08-30 14:53:25 +00:00
|
|
|
if (failed_list) {
|
|
|
|
|
auto it = failed_list;
|
|
|
|
|
while (it) {
|
|
|
|
|
guint id;
|
|
|
|
|
g_object_get(it->data, "id", &id, nullptr);
|
2025-06-20 00:49:53 +00:00
|
|
|
for (auto elem = m_barriers.begin(); elem != m_barriers.end(); elem++) {
|
2024-08-30 14:53:25 +00:00
|
|
|
if (*elem == it->data) {
|
2025-05-20 02:14:18 +00:00
|
|
|
int x1;
|
|
|
|
|
int x2;
|
|
|
|
|
int y1;
|
|
|
|
|
int y2;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
g_object_get(G_OBJECT(*elem), "x1", &x1, "x2", &x2, "y1", &y1, "y2", &y2, nullptr);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
LOG_WARN("failed to apply barrier %d (%d/%d-%d/%d)", id, x1, y1, x2, y2);
|
2024-08-30 14:53:25 +00:00
|
|
|
g_object_unref(*elem);
|
2025-06-20 00:49:53 +00:00
|
|
|
m_barriers.erase(elem);
|
2026-05-13 00:19:18 +00:00
|
|
|
std::erase_if(m_barrierInfo, [id](const auto &info) { return info.id == id; });
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
it = it->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_list_free_full(failed_list, g_object_unref);
|
|
|
|
|
|
|
|
|
|
enable();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 00:19:18 +00:00
|
|
|
std::pair<int, int>
|
|
|
|
|
PortalInputCapture::mapPortalActivationToScreenPosition(guint barrierId, double rawX, double rawY) const
|
|
|
|
|
{
|
|
|
|
|
auto x = static_cast<int>(rawX);
|
|
|
|
|
auto y = static_cast<int>(rawY);
|
|
|
|
|
|
|
|
|
|
const auto it = std::ranges::find_if(m_barrierInfo, [barrierId](const auto &info) { return info.id == barrierId; });
|
|
|
|
|
if (it == m_barrierInfo.end()) {
|
|
|
|
|
LOG_DEBUG("activated barrier %u is not in the current pointer barrier set", barrierId);
|
|
|
|
|
return {x, y};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto zoneLeft = it->x;
|
|
|
|
|
const auto zoneTop = it->y;
|
|
|
|
|
const auto zoneRight = it->x + static_cast<gint>(it->width) - 1;
|
|
|
|
|
const auto zoneBottom = it->y + static_cast<gint>(it->height) - 1;
|
|
|
|
|
|
|
|
|
|
std::int32_t screenX;
|
|
|
|
|
std::int32_t screenY;
|
|
|
|
|
std::int32_t screenW;
|
|
|
|
|
std::int32_t screenH;
|
|
|
|
|
m_screen->getShape(screenX, screenY, screenW, screenH);
|
|
|
|
|
|
|
|
|
|
Bounds portalBounds;
|
|
|
|
|
if (getPortalBounds(portalBounds)) {
|
|
|
|
|
x = scaleCoordinateBetweenRanges(rawX, portalBounds.left, portalBounds.right, screenX, screenX + screenW - 1);
|
|
|
|
|
y = scaleCoordinateBetweenRanges(rawY, portalBounds.top, portalBounds.bottom, screenY, screenY + screenH - 1);
|
|
|
|
|
} else {
|
|
|
|
|
x = std::clamp(x, zoneLeft, zoneRight);
|
|
|
|
|
y = std::clamp(y, zoneTop, zoneBottom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The portal reports per-output zones, while Deskflow models the whole computer as one screen.
|
|
|
|
|
// Use the activated barrier to preserve the intended switch direction in Deskflow's aggregate coordinates.
|
|
|
|
|
using enum BarrierSide;
|
|
|
|
|
switch (it->side) {
|
|
|
|
|
case Left:
|
|
|
|
|
x = screenX;
|
|
|
|
|
break;
|
|
|
|
|
case Right:
|
|
|
|
|
x = screenX + screenW - 1;
|
|
|
|
|
break;
|
|
|
|
|
case Top:
|
|
|
|
|
y = screenY;
|
|
|
|
|
break;
|
|
|
|
|
case Bottom:
|
|
|
|
|
y = screenY + screenH - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {x, y};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::pair<double, double> PortalInputCapture::mapPortalReleasePosition(double x, double y) const
|
|
|
|
|
{
|
|
|
|
|
std::int32_t screenX;
|
|
|
|
|
std::int32_t screenY;
|
|
|
|
|
std::int32_t screenW;
|
|
|
|
|
std::int32_t screenH;
|
|
|
|
|
m_screen->getShape(screenX, screenY, screenW, screenH);
|
|
|
|
|
|
|
|
|
|
const auto screenLeft = screenX;
|
|
|
|
|
const auto screenTop = screenY;
|
|
|
|
|
const auto screenRight = screenX + screenW - 1;
|
|
|
|
|
const auto screenBottom = screenY + screenH - 1;
|
|
|
|
|
const auto jumpZoneSize = m_screen->getJumpZoneSize();
|
|
|
|
|
Bounds portalBounds;
|
|
|
|
|
if (!getPortalBounds(portalBounds)) {
|
|
|
|
|
return {x, y};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto mappedX = scaleCoordinateBetweenRanges(x, screenLeft, screenRight, portalBounds.left, portalBounds.right);
|
|
|
|
|
auto mappedY = scaleCoordinateBetweenRanges(y, screenTop, screenBottom, portalBounds.top, portalBounds.bottom);
|
|
|
|
|
BarrierInfo releaseBarrier;
|
|
|
|
|
if (getClosestReleaseBarrier(x, y, screenLeft, screenTop, screenRight, screenBottom, portalBounds, releaseBarrier)) {
|
|
|
|
|
const Bounds releaseBounds = {
|
|
|
|
|
releaseBarrier.x, releaseBarrier.y, releaseBarrier.x + static_cast<gint>(releaseBarrier.width) - 1,
|
|
|
|
|
releaseBarrier.y + static_cast<gint>(releaseBarrier.height) - 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using enum BarrierSide;
|
|
|
|
|
switch (releaseBarrier.side) {
|
|
|
|
|
case Left:
|
|
|
|
|
mappedX = std::min(releaseBounds.left + jumpZoneSize, releaseBounds.right);
|
|
|
|
|
mappedY = std::clamp(mappedY, releaseBounds.top, releaseBounds.bottom);
|
|
|
|
|
break;
|
|
|
|
|
case Right:
|
|
|
|
|
mappedX = std::max(releaseBounds.right - jumpZoneSize, releaseBounds.left);
|
|
|
|
|
mappedY = std::clamp(mappedY, releaseBounds.top, releaseBounds.bottom);
|
|
|
|
|
break;
|
|
|
|
|
case Top:
|
|
|
|
|
mappedX = std::clamp(mappedX, releaseBounds.left, releaseBounds.right);
|
|
|
|
|
mappedY = std::min(releaseBounds.top + jumpZoneSize, releaseBounds.bottom);
|
|
|
|
|
break;
|
|
|
|
|
case Bottom:
|
|
|
|
|
mappedX = std::clamp(mappedX, releaseBounds.left, releaseBounds.right);
|
|
|
|
|
mappedY = std::max(releaseBounds.bottom - jumpZoneSize, releaseBounds.top);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {mappedX, mappedY};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 00:18:18 +00:00
|
|
|
void PortalInputCapture::addBarrier(
|
|
|
|
|
guint id, BarrierSide side, gint zoneX, gint zoneY, guint zoneWidth, guint zoneHeight
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
gint x1 = 0;
|
|
|
|
|
gint x2 = 0;
|
|
|
|
|
gint y1 = 0;
|
|
|
|
|
gint y2 = 0;
|
|
|
|
|
|
|
|
|
|
using enum BarrierSide;
|
|
|
|
|
switch (side) {
|
|
|
|
|
case Left:
|
|
|
|
|
x1 = zoneX;
|
|
|
|
|
y1 = zoneY;
|
|
|
|
|
x2 = zoneX;
|
|
|
|
|
y2 = zoneY + static_cast<gint>(zoneHeight) - 1;
|
|
|
|
|
break;
|
|
|
|
|
case Right:
|
|
|
|
|
x1 = zoneX + static_cast<gint>(zoneWidth);
|
|
|
|
|
y1 = zoneY;
|
|
|
|
|
x2 = x1;
|
|
|
|
|
y2 = zoneY + static_cast<gint>(zoneHeight) - 1;
|
|
|
|
|
break;
|
|
|
|
|
case Top:
|
|
|
|
|
x1 = zoneX;
|
|
|
|
|
y1 = zoneY;
|
|
|
|
|
x2 = zoneX + static_cast<gint>(zoneWidth) - 1;
|
|
|
|
|
y2 = zoneY;
|
|
|
|
|
break;
|
|
|
|
|
case Bottom:
|
|
|
|
|
x1 = zoneX;
|
|
|
|
|
y1 = zoneY + static_cast<gint>(zoneHeight);
|
|
|
|
|
x2 = zoneX + static_cast<gint>(zoneWidth) - 1;
|
|
|
|
|
y2 = y1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("barrier (%s) %u at %d,%d-%d,%d", barrierSideName(side), id, x1, y1, x2, y2);
|
|
|
|
|
m_barriers.push_back(XDP_INPUT_CAPTURE_POINTER_BARRIER(
|
|
|
|
|
g_object_new(XDP_TYPE_INPUT_CAPTURE_POINTER_BARRIER, "id", id, "x1", x1, "y1", y1, "x2", x2, "y2", y2, nullptr)
|
|
|
|
|
));
|
2026-05-13 00:19:18 +00:00
|
|
|
m_barrierInfo.push_back({id, side, zoneX, zoneY, zoneWidth, zoneHeight, x1, y1, x2, y2});
|
2026-05-13 00:18:18 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
gboolean PortalInputCapture::initSession()
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("setting up input capture session");
|
2026-02-02 01:10:24 +00:00
|
|
|
XdpInputCaptureSession *session;
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE
|
|
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
|
|
|
|
|
m_portalVersion = xdp_portal_get_input_capture_version_sync(
|
|
|
|
|
m_portal,
|
|
|
|
|
nullptr, // Cancellable
|
|
|
|
|
nullptr
|
|
|
|
|
);
|
|
|
|
|
LOG_DEBUG("input capture version %d", m_portalVersion);
|
|
|
|
|
|
|
|
|
|
switch (m_portalVersion) {
|
|
|
|
|
case 0:
|
|
|
|
|
LOG_WARN("portal bug: input capture version is %d", m_portalVersion);
|
|
|
|
|
// fallthrough
|
|
|
|
|
case 1:
|
|
|
|
|
xdp_portal_create_input_capture_session(
|
|
|
|
|
m_portal,
|
|
|
|
|
nullptr, // parent
|
|
|
|
|
static_cast<XdpInputCapability>(XDP_INPUT_CAPABILITY_KEYBOARD | XDP_INPUT_CAPABILITY_POINTER),
|
|
|
|
|
nullptr, // cancellable
|
|
|
|
|
[](GObject *obj, GAsyncResult *res, gpointer data) {
|
|
|
|
|
static_cast<PortalInputCapture *>(data)->handleInitSession(obj, res);
|
|
|
|
|
},
|
|
|
|
|
this
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
session = xdp_portal_create_input_capture_session2_sync(
|
|
|
|
|
m_portal,
|
|
|
|
|
nullptr, // Cancellable
|
|
|
|
|
&error
|
|
|
|
|
);
|
|
|
|
|
if (!session) {
|
|
|
|
|
LOG_ERR("failed to initialize input capture session, quitting: %s", error->message);
|
|
|
|
|
g_main_loop_quit(m_glibMainLoop);
|
|
|
|
|
m_events->addEvent(Event(EventTypes::Quit));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
m_session = session;
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
xdp_session_request_clipboard(xdp_input_capture_session_get_session(session));
|
2026-02-02 01:10:24 +00:00
|
|
|
xdp_input_capture_session_set_session_persistence(session, XDP_INPUT_CAPTURE_SESSION_PERSISTENCE_PERSISTENT);
|
|
|
|
|
if (auto sessionToken = Settings::value(Settings::Server::XdpRestoreToken).toByteArray(); !sessionToken.isEmpty()) {
|
|
|
|
|
xdp_input_capture_session_set_restore_token(session, strdup(sessionToken.data()));
|
|
|
|
|
}
|
|
|
|
|
xdp_input_capture_session_start(
|
|
|
|
|
m_session,
|
|
|
|
|
nullptr, // parent
|
|
|
|
|
static_cast<XdpInputCapability>(XDP_INPUT_CAPABILITY_KEYBOARD | XDP_INPUT_CAPABILITY_POINTER),
|
|
|
|
|
nullptr, // cancellable
|
|
|
|
|
[](GObject *obj, GAsyncResult *res, gpointer data) {
|
|
|
|
|
static_cast<PortalInputCapture *>(data)->handleStart(obj, res);
|
|
|
|
|
},
|
|
|
|
|
this
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#else
|
2024-08-30 14:53:25 +00:00
|
|
|
xdp_portal_create_input_capture_session(
|
2025-06-20 00:49:53 +00:00
|
|
|
m_portal,
|
2024-08-30 14:53:25 +00:00
|
|
|
nullptr, // parent
|
2024-10-17 18:04:35 +00:00
|
|
|
static_cast<XdpInputCapability>(XDP_INPUT_CAPABILITY_KEYBOARD | XDP_INPUT_CAPABILITY_POINTER),
|
2024-08-30 14:53:25 +00:00
|
|
|
nullptr, // cancellable
|
|
|
|
|
[](GObject *obj, GAsyncResult *res, gpointer data) {
|
2025-06-20 00:49:53 +00:00
|
|
|
static_cast<PortalInputCapture *>(data)->handleInitSession(obj, res);
|
2024-08-30 14:53:25 +00:00
|
|
|
},
|
2024-10-17 18:04:35 +00:00
|
|
|
this
|
|
|
|
|
);
|
2026-02-02 01:10:24 +00:00
|
|
|
#endif
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void PortalInputCapture::enable()
|
|
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
if (!m_enabled) {
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("enabling the portal input capture session");
|
2025-06-20 00:49:53 +00:00
|
|
|
xdp_input_capture_session_enable(m_session);
|
|
|
|
|
m_enabled = true;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void PortalInputCapture::disable()
|
|
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
if (m_enabled) {
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("disabling the portal input capture session");
|
2025-06-20 00:49:53 +00:00
|
|
|
xdp_input_capture_session_disable(m_session);
|
|
|
|
|
m_enabled = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void PortalInputCapture::release()
|
|
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
LOG_DEBUG("releasing input capture session, id=%d", m_activationId);
|
|
|
|
|
xdp_input_capture_session_release(m_session, m_activationId);
|
|
|
|
|
m_isActive = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void PortalInputCapture::release(double x, double y)
|
|
|
|
|
{
|
2026-05-13 00:19:18 +00:00
|
|
|
const auto [mappedX, mappedY] = mapPortalReleasePosition(x, y);
|
|
|
|
|
LOG_DEBUG(
|
|
|
|
|
"releasing input capture session, id=%d x=%.1f y=%.1f mapped=%.1f,%.1f", m_activationId, x, y, mappedX, mappedY
|
|
|
|
|
);
|
|
|
|
|
xdp_input_capture_session_release_at(m_session, m_activationId, mappedX, mappedY);
|
2025-06-20 00:49:53 +00:00
|
|
|
m_isActive = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 02:29:34 +00:00
|
|
|
void PortalInputCapture::handleDisabled(const XdpInputCaptureSession *, const GVariant *)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("portal cb disabled");
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
if (!m_enabled)
|
2024-08-30 14:53:25 +00:00
|
|
|
return; // Nothing to do
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
m_enabled = false;
|
|
|
|
|
m_isActive = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
// FIXME: need some better heuristics here of when we want to enable again
|
|
|
|
|
// But we don't know *why* we got disabled (and it's doubtfull we ever
|
|
|
|
|
// will), so we just assume that the zones will change or something and we
|
|
|
|
|
// can re-enable again
|
|
|
|
|
// ... very soon
|
|
|
|
|
g_timeout_add(
|
|
|
|
|
1000,
|
|
|
|
|
[](gpointer data) -> gboolean {
|
2025-05-23 18:32:42 +00:00
|
|
|
static_cast<PortalInputCapture *>(data)->enable();
|
2024-08-30 14:53:25 +00:00
|
|
|
return false;
|
|
|
|
|
},
|
2024-10-17 18:04:35 +00:00
|
|
|
this
|
|
|
|
|
);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 15:20:37 +00:00
|
|
|
void PortalInputCapture::handleActivated(
|
|
|
|
|
const XdpInputCaptureSession *, const std::uint32_t activationId, GVariant *options
|
|
|
|
|
)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
LOG_DEBUG("portal cb activated, id=%d", activationId);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
if (options) {
|
2025-05-20 02:14:18 +00:00
|
|
|
gdouble x;
|
|
|
|
|
gdouble y;
|
2024-08-30 14:53:25 +00:00
|
|
|
if (g_variant_lookup(options, "cursor_position", "(dd)", &x, &y)) {
|
2026-05-13 00:19:18 +00:00
|
|
|
auto warpX = static_cast<int>(x);
|
|
|
|
|
auto warpY = static_cast<int>(y);
|
|
|
|
|
|
|
|
|
|
guint barrierId = 0;
|
|
|
|
|
const bool hasBarrierId = g_variant_lookup(options, "barrier_id", "u", &barrierId);
|
|
|
|
|
|
|
|
|
|
if (hasBarrierId && barrierId > 0) {
|
|
|
|
|
auto [mappedX, mappedY] = mapPortalActivationToScreenPosition(barrierId, x, y);
|
|
|
|
|
warpX = mappedX;
|
|
|
|
|
warpY = mappedY;
|
|
|
|
|
} else if (!hasBarrierId) {
|
|
|
|
|
LOG_DEBUG("portal activation has no barrier id, using raw cursor position");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DEBUG("portal activation barrier id is zero, using raw cursor position");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_screen->warpCursor(warpX, warpY);
|
|
|
|
|
m_events->addEvent(Event(
|
|
|
|
|
EventTypes::PrimaryScreenMotionOnPrimary, m_screen->getEventTarget(),
|
|
|
|
|
IPrimaryScreen::MotionInfo::alloc(warpX, warpY)
|
|
|
|
|
));
|
2024-08-30 14:53:25 +00:00
|
|
|
} else {
|
|
|
|
|
LOG_WARN("failed to get cursor position");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARN("activation has no options");
|
|
|
|
|
}
|
2025-06-20 00:49:53 +00:00
|
|
|
m_activationId = activationId;
|
|
|
|
|
m_isActive = true;
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPORTAL_CLIPBOARD
|
|
|
|
|
if (m_session) {
|
|
|
|
|
LOG_DEBUG("reading clipboard selection on activation");
|
|
|
|
|
m_screen->sendClipboardEvent(EventTypes::ClipboardGrabbed, kClipboardClipboard);
|
|
|
|
|
|
|
|
|
|
XdpSession *session = xdp_input_capture_session_get_session(m_session);
|
|
|
|
|
const char **mimeTypes = xdp_session_get_selection_mime_types(session);
|
|
|
|
|
|
|
|
|
|
if (mimeTypes && mimeTypes[0]) {
|
2026-05-19 19:01:38 +00:00
|
|
|
LOG_DEBUG("clipboard current selection mime types: %s", PortalClipboard::formatMimeTypes(mimeTypes).constData());
|
feat: inputcapture clipboard support
Send the clipboard over the libportal InputCapture portal. Only built
if the installed libportal has clipboard support.
AI disclosure: Claude was used on both sides of this work.
- Peter Hutterer: "The second commit was purely written by Claude
after I stared at this for too long without seeing any hope of
getting started. Turns out Claude wrote something that at least
looks correct and it does work. It needs review but also needs
knowledge of deskflow's internals that I don't have..."
- Nick Bolton: Claude also assisted with the follow-up work listed
below (image copy/paste, session re-request, race fixes, size/mime
validation, and CMake detection).
Server side (Wayland host, Peter Hutterer):
- Initial scaffolding for inputcapture clipboard support, guarded so it
only builds when the portal exposes the clipboard API.
- Integrate clipboard data into the inputcapture session so the host
clipboard can be offered to the remote (the Claude-written commit
referenced above).
Client side (Wayland receiver, KoljaFrahm):
- Receive clipboard data from the server and feed it into the local
clipboard through libportal. Built on the reference code by Peter:
https://github.com/whot/libportal-clipboard-test/tree/main
Build integration and rebase (Chris Rizzitello, sithlord48):
- Add the original CMake check for libportal/clipboard.h to define
HAVE_LIBPORTAL_CLIPBOARD, and guard the libportal clipboard use on
it.
- Update the code style and rebase onto the updated input capture
session persistence: https://github.com/deskflow/deskflow/pull/9415
Image support and reliability fixes (Nick Bolton):
- Support image copy/paste in addition to text, so screenshots and
other image clipboard contents work both ways through inputcapture.
- Re-request the input capture session when it comes up without a
clipboard, so new testers with existing sessions can get clipboard
functionality without needing to clear the session.
- Defer clipboard reads until the input capture session is active,
avoiding races where reads happen before the portal is ready.
- Fix assorted style issues in original commit to match conventions.
- Check the configured clipboard size limit and validate incoming
mime types before forwarding inputcapture clipboard data.
- Switch the CMake clipboard probe to check_symbol_exists so detection
matches what the compiler actually sees in the headers.
Co-authored-by: Nick Bolton <nick@symless.com>
Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
Co-authored-by: Chris Rizzitello <sithlord48@gmail.com>
2026-02-05 04:01:33 +00:00
|
|
|
if (!xdp_session_is_selection_owned_by_session(session))
|
|
|
|
|
readClipboardSelection(session);
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DEBUG("no current clipboard selection");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
claimClipboardOwnership(session);
|
|
|
|
|
LOG_DEBUG("activation clipboard handling complete");
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARN("input capture activated without a session, skipping clipboard read");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 15:20:37 +00:00
|
|
|
void PortalInputCapture::handleDeactivated(
|
|
|
|
|
const XdpInputCaptureSession *, const std::uint32_t activationId, const GVariant *
|
|
|
|
|
)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
LOG_DEBUG("cb deactivated, id=%i", activationId);
|
|
|
|
|
m_isActive = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 02:29:34 +00:00
|
|
|
void PortalInputCapture::handleZonesChanged(XdpInputCaptureSession *session, const GVariant *)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
for (auto b : m_barriers)
|
2024-08-30 14:53:25 +00:00
|
|
|
g_object_unref(b);
|
2025-06-20 00:49:53 +00:00
|
|
|
m_barriers.clear();
|
2026-05-13 00:19:18 +00:00
|
|
|
m_barrierInfo.clear();
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-07-13 14:26:11 +00:00
|
|
|
const auto activeSides = m_screen->activeSides();
|
|
|
|
|
using enum DirectionMask;
|
|
|
|
|
|
|
|
|
|
// May not correctly handle different sized screens
|
2024-08-30 14:53:25 +00:00
|
|
|
auto zones = xdp_input_capture_session_get_zones(session);
|
2026-05-13 00:19:18 +00:00
|
|
|
guint id = 0;
|
2024-08-30 14:53:25 +00:00
|
|
|
while (zones != nullptr) {
|
2025-05-20 02:14:18 +00:00
|
|
|
guint w;
|
|
|
|
|
guint h;
|
|
|
|
|
gint x;
|
|
|
|
|
gint y;
|
2024-10-17 18:04:35 +00:00
|
|
|
g_object_get(zones->data, "width", &w, "height", &h, "x", &x, "y", &y, nullptr);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
LOG_DEBUG("input capture zone, %dx%d@%d,%d", w, h, x, y);
|
|
|
|
|
|
2025-07-13 14:26:11 +00:00
|
|
|
if (activeSides & static_cast<int>(LeftMask)) {
|
2026-05-13 00:18:18 +00:00
|
|
|
addBarrier(++id, BarrierSide::Left, x, y, w, h);
|
2025-07-13 14:26:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (activeSides & static_cast<int>(RightMask)) {
|
2026-05-13 00:18:18 +00:00
|
|
|
addBarrier(++id, BarrierSide::Right, x, y, w, h);
|
2025-07-13 14:26:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (activeSides & static_cast<int>(TopMask)) {
|
2026-05-13 00:18:18 +00:00
|
|
|
addBarrier(++id, BarrierSide::Top, x, y, w, h);
|
2025-07-13 14:26:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (activeSides & static_cast<int>(BottomMask)) {
|
2026-05-13 00:18:18 +00:00
|
|
|
addBarrier(++id, BarrierSide::Bottom, x, y, w, h);
|
2025-07-13 14:26:11 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
zones = zones->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GList *list = nullptr;
|
2025-06-20 00:49:53 +00:00
|
|
|
for (auto const &b : m_barriers) {
|
2024-08-30 14:53:25 +00:00
|
|
|
list = g_list_append(list, b);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 02:07:45 +00:00
|
|
|
if (list != nullptr) {
|
|
|
|
|
xdp_input_capture_session_set_pointer_barriers(
|
|
|
|
|
m_session, list,
|
|
|
|
|
nullptr, // cancellable
|
|
|
|
|
[](GObject *obj, GAsyncResult *res, gpointer data) {
|
|
|
|
|
static_cast<PortalInputCapture *>(data)->handleSetPointerBarriers(obj, res);
|
|
|
|
|
},
|
|
|
|
|
this
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
LOG_WARN("no input capture pointer barriers found");
|
|
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-23 20:52:46 +00:00
|
|
|
void PortalInputCapture::glibThread(const void *)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:49:53 +00:00
|
|
|
auto context = g_main_loop_get_context(m_glibMainLoop);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
LOG_DEBUG("glib thread running");
|
|
|
|
|
|
2025-06-20 00:49:53 +00:00
|
|
|
while (g_main_loop_is_running(m_glibMainLoop)) {
|
2024-08-30 14:53:25 +00:00
|
|
|
Thread::testCancel();
|
|
|
|
|
g_main_context_iteration(context, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("shutting down glib thread");
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
} // namespace deskflow
|