2024-08-30 14:53:25 +00:00
|
|
|
/*
|
2024-09-17 19:00:25 +00:00
|
|
|
* Deskflow -- mouse and keyboard sharing utility
|
2026-02-06 14:33:19 +00:00
|
|
|
* SPDX-FileCopyrightText: (C) 2025 - 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
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "platform/EiScreen.h"
|
|
|
|
|
|
|
|
|
|
#include "base/IEventQueue.h"
|
|
|
|
|
#include "base/Log.h"
|
2025-03-19 22:14:27 +00:00
|
|
|
#include "common/Constants.h"
|
2025-08-23 20:10:13 +00:00
|
|
|
#include "common/Settings.h"
|
2025-09-12 01:12:24 +00:00
|
|
|
#include "deskflow/App.h"
|
2025-06-03 01:28:01 +00:00
|
|
|
#include "deskflow/IScreen.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/OptionTypes.h"
|
|
|
|
|
#include "platform/EiClipboard.h"
|
2024-08-30 14:53:25 +00:00
|
|
|
#include "platform/EiEventQueueBuffer.h"
|
|
|
|
|
#include "platform/EiKeyState.h"
|
|
|
|
|
#include "platform/PortalInputCapture.h"
|
|
|
|
|
#include "platform/PortalRemoteDesktop.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2025-05-20 02:14:18 +00:00
|
|
|
// Values are in pixels
|
2024-10-17 18:04:35 +00:00
|
|
|
struct ScrollRemainder
|
|
|
|
|
{
|
2025-05-20 02:14:18 +00:00
|
|
|
double x;
|
|
|
|
|
double y;
|
2024-08-30 14:53:25 +00:00
|
|
|
};
|
|
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
namespace deskflow {
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-02-06 02:30:30 +00:00
|
|
|
EiScreen::EiScreen(bool isPrimary, IEventQueue *events, bool usePortal)
|
|
|
|
|
: PlatformScreen{events},
|
2025-06-20 00:53:37 +00:00
|
|
|
m_isPrimary{isPrimary},
|
|
|
|
|
m_events{events},
|
|
|
|
|
m_w{1},
|
|
|
|
|
m_h{1},
|
|
|
|
|
m_isOnScreen{isPrimary}
|
|
|
|
|
{
|
|
|
|
|
initEi();
|
|
|
|
|
m_keyState = new EiKeyState(this, events);
|
2024-08-30 14:53:25 +00:00
|
|
|
// install event handlers
|
2025-06-20 00:53:37 +00:00
|
|
|
m_events->addHandler(EventTypes::System, m_events->getSystemTarget(), [this](const auto &e) {
|
|
|
|
|
handleSystemEvent(e);
|
|
|
|
|
});
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (usePortal) {
|
|
|
|
|
m_events->addHandler(EventTypes::EIConnected, getEventTarget(), [this](const auto &e) {
|
|
|
|
|
handleConnectedToEisEvent(e);
|
2025-06-18 04:17:54 +00:00
|
|
|
});
|
2025-06-20 00:53:37 +00:00
|
|
|
if (isPrimary) {
|
|
|
|
|
m_portalInputCapture = new PortalInputCapture(this, m_events);
|
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
|
|
|
// Portal input capture manages its own clipboard
|
2024-08-30 14:53:25 +00:00
|
|
|
} else {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_events->addHandler(EventTypes::EISessionClosed, getEventTarget(), [this](const auto &) {
|
|
|
|
|
handlePortalSessionClosed();
|
2025-06-18 04:17:54 +00:00
|
|
|
});
|
2025-06-20 00:53:37 +00:00
|
|
|
m_portalRemoteDesktop = new PortalRemoteDesktop(this, m_events);
|
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 remote desktop (secondary screen)
|
|
|
|
|
m_clipboard = new EiClipboard(kClipboardClipboard);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Note: socket backend does not support reconnections
|
2026-06-19 00:05:10 +00:00
|
|
|
if (auto rc = ei_setup_backend_socket(m_ei, nullptr); rc != 0) {
|
2024-09-09 12:33:57 +00:00
|
|
|
LOG_ERR("ei init error: %s", strerror(-rc));
|
2024-09-09 10:18:17 +00:00
|
|
|
throw std::runtime_error("failed to init ei context");
|
2024-08-30 14:53:25 +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 socket backend
|
|
|
|
|
m_clipboard = new EiClipboard(kClipboardClipboard);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-09-12 01:12:24 +00:00
|
|
|
|
|
|
|
|
// disable sleep if the flag is set
|
2025-08-23 20:10:13 +00:00
|
|
|
if (Settings::value(Settings::Core::PreventSleep).toBool()) {
|
2025-09-12 01:12:24 +00:00
|
|
|
m_powerManager.disableSleep();
|
|
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
EiScreen::~EiScreen()
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_events->adoptBuffer(nullptr);
|
|
|
|
|
m_events->removeHandler(EventTypes::System, m_events->getSystemTarget());
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
cleanupEi();
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
delete m_keyState;
|
2025-06-03 01:28:01 +00:00
|
|
|
delete m_clipboard;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
delete m_portalRemoteDesktop;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::eiLogEvent(ei_log_priority priority, const char *message) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
switch (priority) {
|
|
|
|
|
case EI_LOG_PRIORITY_DEBUG:
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE("ei: %s", message);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_LOG_PRIORITY_INFO:
|
|
|
|
|
LOG_INFO("ei: %s", message);
|
|
|
|
|
break;
|
|
|
|
|
case EI_LOG_PRIORITY_WARNING:
|
|
|
|
|
LOG_WARN("ei: %s", message);
|
|
|
|
|
break;
|
|
|
|
|
case EI_LOG_PRIORITY_ERROR:
|
|
|
|
|
LOG_ERR("ei: %s", message);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
LOG_PRINT("ei: %s", message);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::initEi()
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_isPrimary) {
|
|
|
|
|
m_ei = ei_new_receiver(nullptr); // we receive from the display server
|
2024-08-30 14:53:25 +00:00
|
|
|
} else {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_ei = ei_new_sender(nullptr); // we send to the display server
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_set_user_data(m_ei, this);
|
|
|
|
|
ei_log_set_priority(m_ei, EI_LOG_PRIORITY_DEBUG);
|
|
|
|
|
ei_log_set_handler(m_ei, handleEiLogEvent);
|
2024-10-28 19:20:35 +00:00
|
|
|
std::string configName = kAppId;
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_configure_name(m_ei, configName.append(" client").c_str());
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
// install the platform event queue
|
2025-06-20 00:53:37 +00:00
|
|
|
m_events->adoptBuffer(nullptr);
|
2025-09-05 02:29:08 +00:00
|
|
|
m_events->adoptBuffer(new EiEventQueueBuffer(m_ei, m_events));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::cleanupEi()
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiPointer) {
|
|
|
|
|
free(ei_device_get_user_data(m_eiPointer));
|
|
|
|
|
ei_device_set_user_data(m_eiPointer, nullptr);
|
|
|
|
|
m_eiPointer = ei_device_unref(m_eiPointer);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiKeyboard) {
|
|
|
|
|
free(ei_device_get_user_data(m_eiKeyboard));
|
|
|
|
|
ei_device_set_user_data(m_eiKeyboard, nullptr);
|
|
|
|
|
m_eiKeyboard = ei_device_unref(m_eiKeyboard);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiAbs) {
|
|
|
|
|
free(ei_device_get_user_data(m_eiAbs));
|
|
|
|
|
ei_device_set_user_data(m_eiAbs, nullptr);
|
|
|
|
|
m_eiAbs = ei_device_unref(m_eiAbs);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiSeat = ei_seat_unref(m_eiSeat);
|
|
|
|
|
for (auto it = m_eiDevices.begin(); it != m_eiDevices.end(); it++) {
|
2024-08-30 14:53:25 +00:00
|
|
|
free(ei_device_get_user_data(*it));
|
|
|
|
|
ei_device_set_user_data(*it, nullptr);
|
|
|
|
|
ei_device_unref(*it);
|
|
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiDevices.clear();
|
|
|
|
|
m_ei = ei_unref(m_ei);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void *EiScreen::getEventTarget() const
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
return const_cast<void *>(static_cast<const void *>(this));
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-03 01:28:01 +00:00
|
|
|
bool EiScreen::getClipboard(ClipboardID id, IClipboard *clipboard) const
|
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
|
|
|
// If using portal input capture, get clipboard from there
|
|
|
|
|
if (m_portalInputCapture) {
|
|
|
|
|
const auto sourceClipboard = m_portalInputCapture->getClipboard(id);
|
|
|
|
|
if (!sourceClipboard) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return IClipboard::copy(clipboard, sourceClipboard);
|
2025-06-03 01:28:01 +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
|
|
|
// Otherwise use our own clipboard
|
|
|
|
|
if (!m_clipboard) {
|
2025-06-03 01:28:01 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
return IClipboard::copy(clipboard, m_clipboard);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::getShape(int32_t &x, int32_t &y, int32_t &w, int32_t &h) const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
x = m_x;
|
|
|
|
|
y = m_y;
|
|
|
|
|
w = m_w;
|
|
|
|
|
h = m_h;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::getCursorPos(int32_t &x, int32_t &y) const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
x = m_cursorX;
|
|
|
|
|
y = m_cursorY;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-13 14:26:11 +00:00
|
|
|
void EiScreen::reconfigure(uint32_t activeSides)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-08-06 10:22:45 +00:00
|
|
|
const static auto sidesText = sidesMaskToString(activeSides);
|
|
|
|
|
LOG_DEBUG("active sides: %s (0x%02x)", sidesText.c_str(), activeSides);
|
2025-07-13 14:26:11 +00:00
|
|
|
m_activeSides = activeSides;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::uint32_t EiScreen::activeSides()
|
|
|
|
|
{
|
|
|
|
|
return m_activeSides;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::warpCursor(int32_t x, int32_t y)
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_cursorX = x;
|
|
|
|
|
m_cursorY = y;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
std::uint32_t EiScreen::registerHotKey(KeyID key, KeyModifierMask mask)
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
static std::uint32_t next_id;
|
|
|
|
|
std::uint32_t id = std::min(++next_id, 1u);
|
|
|
|
|
|
|
|
|
|
// Bug: id rollover means duplicate hotkey ids. Oh well.
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
auto set = m_hotkeys.find(key);
|
|
|
|
|
if (set == m_hotkeys.end()) {
|
2025-06-20 02:20:36 +00:00
|
|
|
m_hotkeys.try_emplace(key, HotKeySet{key});
|
2025-06-20 00:53:37 +00:00
|
|
|
set = m_hotkeys.find(key);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
set->second.addItem(HotKeyItem(mask, id));
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::unregisterHotKey(uint32_t id)
|
|
|
|
|
{
|
2025-06-20 02:20:36 +00:00
|
|
|
for (auto &[key, set] : m_hotkeys) {
|
|
|
|
|
(void)key;
|
|
|
|
|
if (set.removeById(id)) {
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::fakeInputBegin()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// FIXME -- not implemented
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::fakeInputEnd()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// FIXME -- not implemented
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
std::int32_t EiScreen::getJumpZoneSize() const
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-10-15 16:11:39 +00:00
|
|
|
bool EiScreen::isAnyMouseButtonDown(uint32_t &) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::getCursorCenter(int32_t &x, int32_t &y) const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
x = m_x + m_w / 2;
|
|
|
|
|
y = m_y + m_h / 2;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::fakeMouseButton(ButtonID button, bool press)
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
uint32_t code;
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiPointer)
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (button) {
|
|
|
|
|
case kButtonLeft:
|
|
|
|
|
code = 0x110; // BTN_LEFT
|
|
|
|
|
break;
|
|
|
|
|
case kButtonMiddle:
|
|
|
|
|
code = 0x112; // BTN_MIDDLE
|
|
|
|
|
break;
|
|
|
|
|
case kButtonRight:
|
|
|
|
|
code = 0x111; // BTN_RIGHT
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
code = 0x110 + (button - 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_device_button_button(m_eiPointer, code, press);
|
|
|
|
|
ei_device_frame(m_eiPointer, ei_now(m_ei));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::fakeMouseMove(int32_t x, int32_t y)
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// We get one motion event before enter() with the target position
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_isOnScreen) {
|
|
|
|
|
m_cursorX = x;
|
|
|
|
|
m_cursorY = y;
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiAbs)
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_device_pointer_motion_absolute(m_eiAbs, x, y);
|
|
|
|
|
ei_device_frame(m_eiAbs, ei_now(m_ei));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::fakeMouseRelativeMove(int32_t dx, int32_t dy) const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiPointer)
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_device_pointer_motion(m_eiPointer, dx, dy);
|
|
|
|
|
ei_device_frame(m_eiPointer, ei_now(m_ei));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-06 14:55:41 +00:00
|
|
|
void EiScreen::fakeMouseWheel(ScrollDelta delta) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiPointer)
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
|
2026-02-06 14:55:41 +00:00
|
|
|
delta = applyScrollModifier(delta);
|
2024-09-17 19:00:25 +00:00
|
|
|
// libei and deskflow seem to use opposite directions, so we have
|
2024-08-30 14:53:25 +00:00
|
|
|
// to send EI the opposite of the value received if we want to remain
|
|
|
|
|
// compatible with other platforms (including X11).
|
2026-02-06 14:55:41 +00:00
|
|
|
ei_device_scroll_discrete(m_eiPointer, -delta.x, -delta.y);
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_device_frame(m_eiPointer, ei_now(m_ei));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-06 23:36:53 +00:00
|
|
|
void EiScreen::fakeKey(uint32_t keycode, bool isDown) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiKeyboard)
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
|
2025-08-06 23:36:53 +00:00
|
|
|
auto xkbKeycode = keycode + 8;
|
|
|
|
|
m_keyState->updateXkbState(xkbKeycode, isDown);
|
|
|
|
|
ei_device_keyboard_key(m_eiKeyboard, keycode, isDown);
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_device_frame(m_eiKeyboard, ei_now(m_ei));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::enable()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// Nothing really to be done here
|
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
|
|
|
// Portal-based clipboard gets notifications via events
|
|
|
|
|
// Socket-based clipboard is passive (no monitoring needed)
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::disable()
|
|
|
|
|
{
|
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
|
|
|
// Nothing to do here
|
|
|
|
|
// Portal-based clipboard gets notifications via events
|
|
|
|
|
// Socket-based clipboard is passive (no monitoring needed)
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::enter()
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_isOnScreen = true;
|
|
|
|
|
if (!m_isPrimary) {
|
|
|
|
|
++m_sequenceNumber;
|
|
|
|
|
if (m_eiPointer) {
|
|
|
|
|
ei_device_start_emulating(m_eiPointer, m_sequenceNumber);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiKeyboard) {
|
|
|
|
|
ei_device_start_emulating(m_eiKeyboard, m_sequenceNumber);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiAbs) {
|
|
|
|
|
ei_device_start_emulating(m_eiAbs, m_sequenceNumber);
|
|
|
|
|
fakeMouseMove(m_cursorX, m_cursorY);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2024-10-21 20:39:15 +00:00
|
|
|
} else {
|
2025-06-20 00:53:37 +00:00
|
|
|
LOG_DEBUG("releasing input capture at x=%i y=%i", m_cursorX, m_cursorY);
|
|
|
|
|
m_portalInputCapture->release(m_cursorX, m_cursorY);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
bool EiScreen::canLeave()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2024-09-17 03:19:52 +00:00
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::leave()
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_isPrimary) {
|
|
|
|
|
if (m_eiPointer) {
|
|
|
|
|
ei_device_stop_emulating(m_eiPointer);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiKeyboard) {
|
|
|
|
|
ei_device_stop_emulating(m_eiKeyboard);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_eiAbs) {
|
|
|
|
|
ei_device_stop_emulating(m_eiAbs);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
m_isOnScreen = false;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-03 01:28:01 +00:00
|
|
|
bool EiScreen::setClipboard(ClipboardID id, const IClipboard *clipboard)
|
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
|
|
|
if (!clipboard) {
|
2025-06-03 01:28:01 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
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 using portal input capture, set clipboard there
|
|
|
|
|
if (m_portalInputCapture) {
|
|
|
|
|
IClipboard *targetClipboard = m_portalInputCapture->getClipboard(id);
|
|
|
|
|
if (!targetClipboard) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return IClipboard::copy(targetClipboard, clipboard);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise use our own clipboard
|
|
|
|
|
if (!m_clipboard) {
|
2025-06-03 01:28:01 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 19:01:38 +00:00
|
|
|
bool ok = IClipboard::copy(m_clipboard, clipboard);
|
|
|
|
|
|
|
|
|
|
if (ok && m_portalRemoteDesktop && id == kClipboardClipboard) {
|
|
|
|
|
m_portalRemoteDesktop->claimClipboard();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ok;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::checkClipboards()
|
|
|
|
|
{
|
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
|
|
|
// For portal-based input capture, clipboard changes come via portal events
|
|
|
|
|
// For socket-based, clipboard is passive and changes are sent explicitly
|
|
|
|
|
// Nothing to do here
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::openScreensaver(bool notify)
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// FIXME
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::closeScreensaver()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// FIXME
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::screensaver(bool activate)
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// FIXME
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::resetOptions()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// Should reset options to neutral, see setOptions().
|
|
|
|
|
// We don't have ei-specific options, nothing to do here
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::setOptions(const OptionsList &options)
|
|
|
|
|
{
|
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
|
|
|
for (auto it = options.begin(); it != options.end(); ++it) {
|
|
|
|
|
if (*it == kOptionClipboardSharingSize) {
|
|
|
|
|
++it;
|
|
|
|
|
if (it == options.end())
|
|
|
|
|
break;
|
|
|
|
|
m_maximumClipboardSize = *it;
|
|
|
|
|
LOG_DEBUG("ei screen received clipboard size limit: %zu KB", m_maximumClipboardSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
bool EiScreen::isPrimary() const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
return m_isPrimary;
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::updateShape()
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_w = 1;
|
|
|
|
|
m_h = 1;
|
|
|
|
|
m_x = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
m_y = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
for (auto it = m_eiDevices.begin(); it != m_eiDevices.end(); it++) {
|
2024-08-30 14:53:25 +00:00
|
|
|
auto idx = 0;
|
|
|
|
|
struct ei_region *r;
|
|
|
|
|
while ((r = ei_device_get_region(*it, idx++)) != nullptr) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_x = std::min(ei_region_get_x(r), m_x);
|
|
|
|
|
m_y = std::min(ei_region_get_y(r), m_y);
|
|
|
|
|
m_w = std::max(ei_region_get_x(r) + ei_region_get_width(r), m_w);
|
|
|
|
|
m_h = std::max(ei_region_get_y(r) + ei_region_get_height(r), m_h);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
LOG_DEBUG("logical output size: %dx%d@%d.%d", m_w, m_h, m_x, m_y);
|
|
|
|
|
m_cursorX = m_x + m_w / 2;
|
|
|
|
|
m_cursorY = m_y + m_h / 2;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-02 01:50:53 +00:00
|
|
|
sendEvent(EventTypes::ScreenShapeChanged, nullptr);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::addDevice(struct ei_device *device)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("adding device %s", ei_device_get_name(device));
|
|
|
|
|
|
|
|
|
|
// Noteworthy: EI in principle supports multiple devices with multiple
|
|
|
|
|
// capabilities, so there may be more than one logical pointer (or even
|
|
|
|
|
// multiple seats). Supporting this is ... tricky so for now we go the easy
|
|
|
|
|
// route: one device for each capability. Note this may be the same device
|
|
|
|
|
// if the first device comes with multiple capabilities.
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiPointer && ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) &&
|
2024-08-30 14:53:25 +00:00
|
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) &&
|
|
|
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL)) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiPointer = ei_device_ref(device);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiKeyboard && ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
|
|
|
|
|
m_eiKeyboard = ei_device_ref(device);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-02 12:03:44 +00:00
|
|
|
if (auto keymap = ei_device_keyboard_get_keymap(device);
|
|
|
|
|
keymap && ei_keymap_get_type(keymap) == EI_KEYMAP_TYPE_XKB) {
|
2024-08-30 14:53:25 +00:00
|
|
|
int fd = ei_keymap_get_fd(keymap);
|
|
|
|
|
size_t len = ei_keymap_get_size(keymap);
|
2025-06-20 00:53:37 +00:00
|
|
|
m_keyState->init(fd, len);
|
2024-08-30 14:53:25 +00:00
|
|
|
} else {
|
|
|
|
|
// We rely on the EIS implementation to give us a keymap, otherwise we
|
|
|
|
|
// really have no idea what a keycode means (other than it's linux/input.h
|
|
|
|
|
// code) Where the EIS implementation does not tell us, we just default to
|
|
|
|
|
// whatever libxkbcommon thinks is default. At least this way we can
|
|
|
|
|
// influence with env vars what we get
|
2024-10-17 18:04:35 +00:00
|
|
|
LOG_WARN("keyboard device %s does not have a keymap, we are guessing", ei_device_get_name(device));
|
2025-06-20 00:53:37 +00:00
|
|
|
m_keyState->initDefaultKeymap();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
m_keyState->updateKeyMap();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiAbs && ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) &&
|
2024-08-30 14:53:25 +00:00
|
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_BUTTON) &&
|
|
|
|
|
ei_device_has_capability(device, EI_DEVICE_CAP_SCROLL)) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiAbs = ei_device_ref(device);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiDevices.emplace_back(ei_device_ref(device));
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
updateShape();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::removeDevice(struct ei_device *device)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("removing device %s", ei_device_get_name(device));
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (device == m_eiPointer)
|
|
|
|
|
m_eiPointer = ei_device_unref(m_eiPointer);
|
|
|
|
|
if (device == m_eiKeyboard)
|
|
|
|
|
m_eiKeyboard = ei_device_unref(m_eiKeyboard);
|
|
|
|
|
if (device == m_eiAbs)
|
|
|
|
|
m_eiAbs = ei_device_unref(m_eiAbs);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
for (auto it = m_eiDevices.begin(); it != m_eiDevices.end(); it++) {
|
2024-08-30 14:53:25 +00:00
|
|
|
if (*it == device) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiDevices.erase(it);
|
2024-08-30 14:53:25 +00:00
|
|
|
ei_device_unref(device);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
updateShape();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-02 01:50:53 +00:00
|
|
|
void EiScreen::sendEvent(EventTypes type, void *data)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_events->addEvent(Event(type, getEventTarget(), data));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-03 01:28:01 +00:00
|
|
|
void EiScreen::sendClipboardEvent(EventTypes type, ClipboardID id) const
|
|
|
|
|
{
|
|
|
|
|
auto *info = static_cast<ClipboardInfo *>(malloc(sizeof(ClipboardInfo)));
|
|
|
|
|
if (info == nullptr) {
|
|
|
|
|
LOG_ERR("malloc failed for ClipboardInfo");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
info->m_id = id;
|
|
|
|
|
info->m_sequenceNumber = m_sequenceNumber;
|
|
|
|
|
|
|
|
|
|
// Use const_cast to call non-const sendEvent from const method
|
|
|
|
|
const_cast<EiScreen *>(this)->sendEvent(type, info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EiScreen::setSequenceNumber(uint32_t seqNum)
|
|
|
|
|
{
|
|
|
|
|
m_sequenceNumber = seqNum;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
ButtonID EiScreen::mapButtonFromEvdev(ei_event *event) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-05-02 12:03:44 +00:00
|
|
|
switch (ei_event_button_get_button(event)) {
|
2024-08-30 14:53:25 +00:00
|
|
|
case 0x110:
|
|
|
|
|
return kButtonLeft;
|
|
|
|
|
case 0x111:
|
|
|
|
|
return kButtonRight;
|
|
|
|
|
case 0x112:
|
|
|
|
|
return kButtonMiddle;
|
|
|
|
|
case 0x113:
|
|
|
|
|
return kButtonExtra0;
|
|
|
|
|
case 0x114:
|
|
|
|
|
return kButtonExtra1;
|
|
|
|
|
default:
|
|
|
|
|
return kButtonNone;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return kButtonNone;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-06 23:36:53 +00:00
|
|
|
bool EiScreen::onHotkey(KeyID keyid, bool isPressed, KeyModifierMask mask)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
auto it = m_hotkeys.find(keyid);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (it == m_hotkeys.end()) {
|
2024-08-30 14:53:25 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Note: our mask (see on_key_event) only contains some modifiers
|
|
|
|
|
// but we don't put a limitation on modifiers in the hotkeys. So some
|
|
|
|
|
// key combinations may not work correctly, more effort is needed here.
|
2025-06-20 00:53:37 +00:00
|
|
|
if (auto id = it->second.findByMask(mask); id != 0) {
|
2025-08-06 23:36:53 +00:00
|
|
|
EventTypes type = isPressed ? EventTypes::PrimaryScreenHotkeyDown : EventTypes::PrimaryScreenHotkeyUp;
|
2024-08-30 14:53:25 +00:00
|
|
|
sendEvent(type, HotKeyInfo::alloc(id));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::onKeyEvent(ei_event *event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-05-20 03:08:59 +00:00
|
|
|
auto keycode = ei_event_keyboard_get_key(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
uint32_t keyval = keycode + 8;
|
|
|
|
|
bool pressed = ei_event_keyboard_get_key_is_press(event);
|
2025-06-20 00:53:37 +00:00
|
|
|
KeyID keyid = m_keyState->mapKeyFromKeyval(keyval);
|
2025-04-27 16:56:55 +00:00
|
|
|
auto keybutton = static_cast<KeyButton>(keyval);
|
2026-01-26 17:28:02 +00:00
|
|
|
bool repeat;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
m_keyState->updateXkbState(keyval, pressed);
|
|
|
|
|
KeyModifierMask mask = m_keyState->pollActiveModifiers();
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-01-26 17:28:02 +00:00
|
|
|
repeat = pressed && m_lastPressed == keyid && keyid != kKeyNone;
|
|
|
|
|
|
|
|
|
|
m_lastPressed = pressed ? keyid : kKeyNone;
|
|
|
|
|
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE(
|
2026-01-26 17:28:02 +00:00
|
|
|
"event: key %s%s keycode=%d keyid=%d mask=0x%x", pressed ? "press" : "release", repeat ? " (repeat)" : "",
|
|
|
|
|
keycode, keyid, mask
|
|
|
|
|
);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_isPrimary && onHotkey(keyid, pressed, mask)) {
|
2024-08-30 14:53:25 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keyid != kKeyNone) {
|
2026-01-26 17:28:02 +00:00
|
|
|
m_keyState->sendKeyEvent(getEventTarget(), pressed, repeat, keyid, mask, 1, keybutton);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::onButtonEvent(ei_event *event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
assert(m_isPrimary);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
auto buttonID = mapButtonFromEvdev(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
bool pressed = ei_event_button_get_is_press(event);
|
2025-06-20 00:53:37 +00:00
|
|
|
KeyModifierMask mask = m_keyState->pollActiveModifiers();
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE("event: button %s button=%d mask=0x%x", pressed ? "press" : "release", buttonID, mask);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-20 03:08:59 +00:00
|
|
|
if (buttonID == kButtonNone) {
|
2024-08-30 14:53:25 +00:00
|
|
|
LOG_DEBUG("event: button not recognized");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 01:50:53 +00:00
|
|
|
auto eventType = pressed ? EventTypes::PrimaryScreenButtonDown : EventTypes::PrimaryScreenButtonUp;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-20 03:08:59 +00:00
|
|
|
sendEvent(eventType, ButtonInfo::alloc(buttonID, mask));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::onPointerScrollEvent(ei_event *event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// Ratio of 10 pixels == one wheel click because that's what mutter/gtk
|
|
|
|
|
// use (for historical reasons).
|
2025-08-06 23:36:53 +00:00
|
|
|
static const int s_pixelsPerWheelClick = 10;
|
2024-08-30 14:53:25 +00:00
|
|
|
// Our logical wheel clicks are multiples 120, so we
|
|
|
|
|
// convert between the two and keep the remainders because
|
|
|
|
|
// we will very likely get subpixel scroll events.
|
2025-08-06 23:36:53 +00:00
|
|
|
// This means a single pixel is 120/s_pixelToWheelRation in wheel values.
|
2026-02-08 15:36:54 +00:00
|
|
|
const int s_pixelToWheelRatio = s_scrollDelta / s_pixelsPerWheelClick;
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
assert(m_isPrimary);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-20 03:08:59 +00:00
|
|
|
auto dx = ei_event_scroll_get_dx(event);
|
|
|
|
|
auto dy = ei_event_scroll_get_dy(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
struct ei_device *device = ei_event_get_device(event);
|
|
|
|
|
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE("event: scroll (%.2f, %.2f)", dx, dy);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-04-27 16:56:55 +00:00
|
|
|
auto *remainder = static_cast<struct ScrollRemainder *>(ei_device_get_user_data(device));
|
2024-08-30 14:53:25 +00:00
|
|
|
if (!remainder) {
|
|
|
|
|
remainder = new ScrollRemainder();
|
|
|
|
|
ei_device_set_user_data(device, remainder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dx += remainder->x;
|
|
|
|
|
dy += remainder->y;
|
|
|
|
|
|
2025-05-20 02:14:18 +00:00
|
|
|
double x;
|
|
|
|
|
double y;
|
2024-08-30 14:53:25 +00:00
|
|
|
double rx = modf(dx, &x);
|
|
|
|
|
double ry = modf(dy, &y);
|
|
|
|
|
|
|
|
|
|
assert(!std::isnan(x) && !std::isinf(x));
|
|
|
|
|
assert(!std::isnan(y) && !std::isinf(y));
|
|
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
// libei and deskflow seem to use opposite directions, so we have
|
2024-08-30 14:53:25 +00:00
|
|
|
// to send the opposite of the value reported by EI if we want to
|
|
|
|
|
// remain compatible with other platforms (including X11).
|
|
|
|
|
if (x != 0 || y != 0)
|
|
|
|
|
sendEvent(
|
2025-05-02 01:50:53 +00:00
|
|
|
EventTypes::PrimaryScreenWheel,
|
2025-08-06 23:36:53 +00:00
|
|
|
WheelInfo::alloc((int32_t)-x * s_pixelToWheelRatio, (int32_t)-y * s_pixelToWheelRatio)
|
2024-10-17 18:04:35 +00:00
|
|
|
);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
remainder->x = rx;
|
|
|
|
|
remainder->y = ry;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::onPointerScrollDiscreteEvent(ei_event *event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-09-17 19:00:25 +00:00
|
|
|
// both libei and deskflow use multiples of 120 to represent
|
2024-08-30 14:53:25 +00:00
|
|
|
// one scroll wheel click event so we can just forward things
|
|
|
|
|
// as-is.
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
assert(m_isPrimary);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-20 03:08:59 +00:00
|
|
|
auto dx = ei_event_scroll_get_discrete_dx(event);
|
|
|
|
|
auto dy = ei_event_scroll_get_discrete_dy(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE("event: scroll discrete (%d, %d)", dx, dy);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
// libei and deskflow seem to use opposite directions, so we have
|
2024-08-30 14:53:25 +00:00
|
|
|
// to send the opposite of the value reported by EI if we want to
|
|
|
|
|
// remain compatible with other platforms (including X11).
|
2025-05-02 01:50:53 +00:00
|
|
|
sendEvent(EventTypes::PrimaryScreenWheel, WheelInfo::alloc(-dx, -dy));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::onMotionEvent(ei_event *event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
assert(m_isPrimary);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-05-20 03:08:59 +00:00
|
|
|
auto dx = ei_event_pointer_get_dx(event);
|
|
|
|
|
auto dy = ei_event_pointer_get_dy(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_isOnScreen) {
|
|
|
|
|
LOG_DEBUG("event: motion on primary x=%i y=%i)", m_cursorX, m_cursorY);
|
|
|
|
|
sendEvent(EventTypes::PrimaryScreenMotionOnPrimary, MotionInfo::alloc(m_cursorX, m_cursorY));
|
2025-08-06 23:41:44 +00:00
|
|
|
if (m_portalInputCapture->isActive()) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_portalInputCapture->release();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_bufferDX += dx;
|
|
|
|
|
m_bufferDY += dy;
|
2025-08-06 23:36:53 +00:00
|
|
|
auto pixelDx = static_cast<std::int32_t>(m_bufferDX);
|
|
|
|
|
auto pixelDy = static_cast<std::int32_t>(m_bufferDY);
|
|
|
|
|
if (pixelDx || pixelDy) {
|
2026-05-09 01:10:59 +00:00
|
|
|
LOG_VERBOSE("event: motion on secondary x=%d y=%d", pixelDx, pixelDy);
|
2025-08-06 23:36:53 +00:00
|
|
|
sendEvent(EventTypes::PrimaryScreenMotionOnSecondary, MotionInfo::alloc(pixelDx, pixelDy));
|
|
|
|
|
m_bufferDX -= pixelDx;
|
|
|
|
|
m_bufferDY -= pixelDy;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 02:20:36 +00:00
|
|
|
void EiScreen::onAbsMotionEvent(const ei_event *) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
assert(m_isPrimary);
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::handleConnectedToEisEvent(const Event &event)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
int fd = static_cast<EiConnectInfo *>(event.getData())->m_fd;
|
|
|
|
|
LOG_DEBUG("eis connection established, fd=%d", fd);
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
auto rc = ei_setup_backend_fd(m_ei, fd);
|
2024-08-30 14:53:25 +00:00
|
|
|
if (rc != 0) {
|
2025-01-21 12:14:08 +00:00
|
|
|
LOG_WARN("failed to set up ei: %s", strerror(-rc));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::handlePortalSessionClosed()
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-04-17 19:55:24 +00:00
|
|
|
// Portal may or may not EI_EVENT_DISCONNECT us before sending the DBus Closed
|
|
|
|
|
// signal. Let's clean up either way.
|
|
|
|
|
LOG_DEBUG("eis screen handling portal session closed");
|
2025-06-20 00:53:37 +00:00
|
|
|
cleanupEi();
|
|
|
|
|
initEi();
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-15 16:11:39 +00:00
|
|
|
void EiScreen::handleSystemEvent(const Event &)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
std::scoped_lock lock{m_mutex};
|
2024-08-30 14:53:25 +00:00
|
|
|
|
|
|
|
|
// Only one ei_dispatch per system event, see the comment in
|
|
|
|
|
// EiEventQueueBuffer::addEvent
|
2025-06-20 00:53:37 +00:00
|
|
|
ei_dispatch(m_ei);
|
2024-08-30 14:53:25 +00:00
|
|
|
struct ei_event *event;
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
while ((event = ei_get_event(m_ei)) != nullptr) {
|
2024-08-30 14:53:25 +00:00
|
|
|
auto type = ei_event_get_type(event);
|
|
|
|
|
auto seat = ei_event_get_seat(event);
|
|
|
|
|
auto device = ei_event_get_device(event);
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case EI_EVENT_CONNECT:
|
|
|
|
|
LOG_DEBUG("connected to eis");
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_SEAT_ADDED:
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_eiSeat) {
|
|
|
|
|
m_eiSeat = ei_seat_ref(seat);
|
2024-08-30 14:53:25 +00:00
|
|
|
ei_seat_bind_capabilities(
|
2025-06-20 00:53:37 +00:00
|
|
|
m_eiSeat, EI_DEVICE_CAP_POINTER, EI_DEVICE_CAP_POINTER_ABSOLUTE, EI_DEVICE_CAP_KEYBOARD,
|
2024-10-17 18:04:35 +00:00
|
|
|
EI_DEVICE_CAP_BUTTON, EI_DEVICE_CAP_SCROLL, nullptr
|
|
|
|
|
);
|
2025-06-20 00:53:37 +00:00
|
|
|
LOG_DEBUG("ei: using seat %s", ei_seat_get_name(m_eiSeat));
|
2024-08-30 14:53:25 +00:00
|
|
|
// we don't care about touch
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_ADDED:
|
2025-06-20 00:53:37 +00:00
|
|
|
if (seat == m_eiSeat) {
|
|
|
|
|
addDevice(device);
|
2024-08-30 14:53:25 +00:00
|
|
|
} else {
|
2025-06-20 00:53:37 +00:00
|
|
|
LOG_INFO("seat %s is ignored", ei_seat_get_name(m_eiSeat));
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_REMOVED:
|
2025-06-20 00:53:37 +00:00
|
|
|
removeDevice(device);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_SEAT_REMOVED:
|
2025-06-20 00:53:37 +00:00
|
|
|
if (seat == m_eiSeat) {
|
|
|
|
|
m_eiSeat = ei_seat_unref(m_eiSeat);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DISCONNECT:
|
|
|
|
|
// We're using libei which emulates the various seat/device remove events
|
|
|
|
|
// so by the time we get here our EiScreen should be in a neutral state.
|
|
|
|
|
//
|
2025-04-17 19:57:47 +00:00
|
|
|
// We must release the xdg-portal InputCapture in case it is still active
|
|
|
|
|
// so that the cursor is usable and not stuck on the deskflow server.
|
|
|
|
|
LOG_WARN("disconnected from eis, will afterwards commence attempt to reconnect");
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_isPrimary) {
|
2025-04-17 19:57:47 +00:00
|
|
|
LOG_DEBUG("re-allocating portal input capture connection and releasing active captures");
|
2025-06-20 00:53:37 +00:00
|
|
|
if (m_portalInputCapture) {
|
2025-08-06 23:41:44 +00:00
|
|
|
if (m_portalInputCapture->isActive()) {
|
2025-06-20 00:53:37 +00:00
|
|
|
m_portalInputCapture->release();
|
2025-04-17 19:57:47 +00:00
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
delete m_portalInputCapture;
|
|
|
|
|
m_portalInputCapture = new PortalInputCapture(this, this->m_events);
|
2025-04-17 19:57:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-20 00:53:37 +00:00
|
|
|
this->handlePortalSessionClosed();
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_PAUSED:
|
|
|
|
|
LOG_DEBUG("device %s is paused", ei_device_get_name(device));
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_RESUMED:
|
|
|
|
|
LOG_DEBUG("device %s is resumed", ei_device_get_name(device));
|
2025-06-20 00:53:37 +00:00
|
|
|
if (!m_isPrimary && m_isOnScreen) {
|
|
|
|
|
ei_device_start_emulating(device, ++m_sequenceNumber);
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_KEYBOARD_MODIFIERS:
|
|
|
|
|
// FIXME
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// events below are for a receiver context (barriers)
|
|
|
|
|
case EI_EVENT_FRAME:
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_START_EMULATING:
|
2024-09-09 12:33:57 +00:00
|
|
|
LOG_DEBUG("device %s started emulating", ei_device_get_name(device));
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_DEVICE_STOP_EMULATING:
|
2024-09-09 12:33:57 +00:00
|
|
|
LOG_DEBUG("device %s stopped emulating", ei_device_get_name(device));
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_KEYBOARD_KEY:
|
2025-06-20 00:53:37 +00:00
|
|
|
onKeyEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_BUTTON_BUTTON:
|
2025-06-20 00:53:37 +00:00
|
|
|
onButtonEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_POINTER_MOTION:
|
2025-06-20 00:53:37 +00:00
|
|
|
onMotionEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
|
2025-06-20 00:53:37 +00:00
|
|
|
onAbsMotionEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_TOUCH_UP:
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_TOUCH_MOTION:
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_TOUCH_DOWN:
|
|
|
|
|
break;
|
|
|
|
|
case EI_EVENT_SCROLL_DELTA:
|
2025-06-20 00:53:37 +00:00
|
|
|
onPointerScrollEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_SCROLL_DISCRETE:
|
2025-06-20 00:53:37 +00:00
|
|
|
onPointerScrollDiscreteEvent(event);
|
2024-08-30 14:53:25 +00:00
|
|
|
break;
|
|
|
|
|
case EI_EVENT_SCROLL_STOP:
|
|
|
|
|
case EI_EVENT_SCROLL_CANCEL:
|
|
|
|
|
break;
|
2025-04-07 02:48:34 +00:00
|
|
|
default:
|
|
|
|
|
break;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
ei_event_unref(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
void EiScreen::updateButtons()
|
|
|
|
|
{
|
2024-08-30 14:53:25 +00:00
|
|
|
// libei relies on the EIS implementation to keep our button count correct,
|
|
|
|
|
// so there's not much we need to/can do here.
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 18:04:35 +00:00
|
|
|
IKeyState *EiScreen::getKeyState() const
|
|
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
return m_keyState;
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-01-06 00:55:07 +00:00
|
|
|
std::string EiScreen::getSecureInputApp() const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2024-09-09 10:18:17 +00:00
|
|
|
throw std::runtime_error("get security input app not implemented");
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
EiScreen::HotKeyItem::HotKeyItem(std::uint32_t mask, std::uint32_t id) : mask(mask), id(id)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-05-20 14:39:18 +00:00
|
|
|
// Todo: Implement
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
EiScreen::HotKeySet::HotKeySet(KeyID key) : m_id(key)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-05-20 14:39:18 +00:00
|
|
|
// Todo: Implement
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
bool EiScreen::HotKeySet::removeById(std::uint32_t id)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
for (auto it = m_set.begin(); it != m_set.end(); ++it) {
|
|
|
|
|
if (it->id == id) {
|
|
|
|
|
m_set.erase(it);
|
2024-08-30 14:53:25 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
void EiScreen::HotKeySet::addItem(HotKeyItem item)
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
m_set.push_back(item);
|
2024-10-17 18:04:35 +00:00
|
|
|
}
|
2024-08-30 14:53:25 +00:00
|
|
|
|
2025-06-20 00:53:37 +00:00
|
|
|
std::uint32_t EiScreen::HotKeySet::findByMask(std::uint32_t mask) const
|
2024-10-17 18:04:35 +00:00
|
|
|
{
|
2025-06-20 00:53:37 +00:00
|
|
|
for (const auto &item : m_set) {
|
|
|
|
|
if (item.mask == mask) {
|
|
|
|
|
return item.id;
|
2024-08-30 14:53:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 19:00:25 +00:00
|
|
|
} // namespace deskflow
|