WIP: Virtualinput pointer and keyboard #778
WIP: Virtualinput pointer and keyboard #778zorowk wants to merge 3 commits intolinuxdeepin:masterfrom
Conversation
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: zorowk The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Reviewer's GuideAdds a dedicated WVirtualInputHelper and WVirtualPointerManagerV1 to manage virtual keyboard and pointer devices, wires them into the compositor and tinywl shell, enables the single-pixel-buffer-v1 protocol, and refactors input device and input method handling to better support virtual input and per-device configuration. Sequence diagram for virtual pointer creation and attachmentsequenceDiagram
actor Client
participant WServer
participant WVirtualPointerManagerV1
participant WVirtualInputHelper
participant WInputDevice
participant WSeat
participant WCursor
participant WOutput
Client->>WServer: bind zwlr_virtual_pointer_manager_v1
Client->>WVirtualPointerManagerV1: create_virtual_pointer(suggested_seat, suggested_output)
WVirtualPointerManagerV1-->>WVirtualInputHelper: newVirtualPointer(event)
activate WVirtualInputHelper
WVirtualInputHelper->>WVirtualInputHelper: shouldAcceptSeat(event.suggested_seat)
alt seat accepted
WVirtualInputHelper->>WInputDevice: ensureDevice(qw_input_device::from(&event.new_pointer.pointer.base))
WVirtualInputHelper->>WVirtualInputHelper: attachDevice(device)
WVirtualInputHelper->>WSeat: attachInputDevice(device)
WVirtualInputHelper->>WVirtualInputHelper: maybeMapToOutput(device, event.suggested_output)
WVirtualInputHelper->>WCursor: cursor = seat.cursor()
WVirtualInputHelper->>WOutput: WOutput::fromHandle(qw_output::from(event.suggested_output))
WVirtualInputHelper->>WCursor: handle.map_input_to_output(device.handle, event.suggested_output)
else seat rejected
WVirtualInputHelper-->>WVirtualPointerManagerV1: ignore event
end
deactivate WVirtualInputHelper
Class diagram for updated InputDevice and per-device config handlingclassDiagram
class InputDevice {
-static InputDevice *m_instance
-unique_ptr~GestureRecognizer~ m_touchpadRecognizer
-QHash~WInputDevice*, QVector~QMetaObject::Connection~~ m_deviceConnections
-uint m_touchpadFingerCount
+static InputDevice *instance()
+void initDevice(WInputDevice *device)
+void deinitDevice(WInputDevice *device)
+SwipeGesture* registerTouchpadSwipe(SwipeFeedBack feed_back)
+HoldGesture* registerTouchpadHold(HoldFeedBack feed)
}
class Helper {
+void init(Treeland::Treeland *treeland)
}
class WBackend {
+inputAdded(WInputDevice *device)
+inputRemoved(WInputDevice *device)
}
class WSeat {
+void attachInputDevice(WInputDevice *device)
+void detachInputDevice(WInputDevice *device)
}
class TreelandUserConfig {
+int inputAccelProfile()
+void inputAccelProfileChanged(int profile)
+void configInitializeSucceed()
}
class WInputDevice {
+qw_input_device* handle()
+QInputDevice* qtDevice()
}
InputDevice ..> WInputDevice : manages
InputDevice ..> TreelandUserConfig : reads accel profile
Helper ..> WBackend : uses
Helper ..> WSeat : uses
Helper ..> InputDevice : uses
WBackend --> Helper : emits inputAdded
WBackend --> Helper : emits inputRemoved
Helper --> WSeat : attachInputDevice
Helper --> WSeat : detachInputDevice
Helper --> InputDevice : initDevice
Helper --> InputDevice : deinitDevice
TreelandUserConfig <.. InputDevice : signal connections
WInputDevice <.. InputDevice : initDevice/deinitDevice
Class diagram for new WVirtualInputHelper and WVirtualPointerManagerV1classDiagram
class WVirtualInputHelper {
+WVirtualInputHelper(WServer *server, WSeat *seat)
+~WVirtualInputHelper()
-void handleNewVKV1(wlr_virtual_keyboard_v1 *virtualKeyboard)
-void handleNewVPV1(wlr_virtual_pointer_v1_new_pointer_event *event)
-bool shouldAcceptSeat(wlr_seat *suggestedSeat) const
-WInputDevice *ensureDevice(qw_input_device *handle) const
-void attachDevice(WInputDevice *device)
-void maybeMapToOutput(WInputDevice *device, wlr_output *output) const
}
class WVirtualInputHelperPrivate {
+QPointer~WServer~ server
+QPointer~WSeat~ seat
+QPointer~WVirtualKeyboardManagerV1~ virtualKeyboardManagerV1
+QPointer~WVirtualPointerManagerV1~ virtualPointerManagerV1
+QList~WInputDevice*~ virtualKeyboards
+QList~WInputDevice*~ virtualPointers
}
class WVirtualKeyboardManagerV1 {
+void newVirtualKeyboard(wlr_virtual_keyboard_v1 *virtualKeyboard)
}
class WVirtualPointerManagerV1 {
+WVirtualPointerManagerV1(QObject *parent)
+QByteArrayView interfaceName() const
+void newVirtualPointer(wlr_virtual_pointer_v1_new_pointer_event *event)
-void create(WServer *server)
-wl_global *global() const
}
class WServer {
+qw_server* handle()
+T* attach~T~()
}
class WSeat {
+void attachInputDevice(WInputDevice *device)
+WCursor* cursor()
+wlr_seat* nativeHandle()
}
class WCursor {
+qw_cursor* handle()
}
class WInputDevice {
+static WInputDevice* fromHandle(qw_input_device *handle)
+qw_input_device* handle()
+WSeat* seat()
}
class WOutput {
+static WOutput* fromHandle(qw_output *handle)
}
WVirtualInputHelper o-- WVirtualInputHelperPrivate : d_ptr
WVirtualInputHelperPrivate ..> WServer : holds
WVirtualInputHelperPrivate ..> WSeat : holds
WVirtualInputHelperPrivate ..> WVirtualKeyboardManagerV1 : manages
WVirtualInputHelperPrivate ..> WVirtualPointerManagerV1 : manages
WVirtualInputHelper ..> WInputDevice : creates and attaches
WVirtualInputHelper ..> WOutput : maybeMapToOutput
WVirtualInputHelper ..> WCursor : via seat cursor
WVirtualPointerManagerV1 ..|> WServerInterface
WVirtualPointerManagerV1 ..|> WObject
WVirtualPointerManagerV1 ..|> QObject
WServer ..> WVirtualKeyboardManagerV1 : attach
WServer ..> WVirtualPointerManagerV1 : attach
WSeat <.. WVirtualInputHelper : attachInputDevice
WVirtualKeyboardManagerV1 --> WVirtualInputHelper : newVirtualKeyboard
WVirtualPointerManagerV1 --> WVirtualInputHelper : newVirtualPointer
Class diagram for updated WInputMethodHelper keyboard grab logicclassDiagram
class WInputMethodHelper {
+WInputMethodHelper(WServer *server, WSeat *seat)
+~WInputMethodHelper()
+WTextInput* focusedTextInput() const
+WInputMethodV2* activeInputMethod() const
+qw_input_method_keyboard_grab_v2* activeKeyboardGrab() const
-void handleNewTI(WTextInput *ti)
-void handleNewIMV2(qw_input_method_v2 *imv2)
-void handleNewKGV2(qw_input_method_keyboard_grab_v2 *kgv2)
-void handleNewIPSV2(qw_input_popup_surface_v2 *ipsv2)
-void updateAllPopupSurfaces(QRect cursorRect)
-void updatePopupSurface(WInputPopupSurface *popup, QRect cursorRect)
-void notifyLeave()
}
class WInputMethodHelperPrivate {
+QPointer~WServer~ server
+QPointer~WSeat~ seat
+QPointer~WInputMethodManagerV2~ inputMethodManagerV2
+QPointer~WTextInputManagerV1~ textInputManagerV1
+QPointer~WTextInputManagerV2~ textInputManagerV2
+QPointer~WTextInputManagerV3~ textInputManagerV3
+WTextInput* enabledTextInput
+WInputMethodV2* activeInputMethod
+qw_input_method_keyboard_grab_v2* activeKeyboardGrab
+wlr_seat_keyboard_grab keyboardGrab
+wlr_seat_keyboard_grab_interface grabInterface
+GrabHandlerArg handlerArg
+QList~WTextInput*~ textInputs
+QList~WInputPopupSurface*~ popupSurfaces
}
class GrabHandlerArg {
+qw_input_method_keyboard_grab_v2 *grab
}
class WSeat {
+wlr_seat* nativeHandle()
+wlr_seat_keyboard_state keyboard_state
}
class qw_input_method_keyboard_grab_v2 {
+void set_keyboard(wlr_keyboard *keyboard)
+void send_key(uint32_t time_msec, int key, uint32_t state)
+void send_modifiers(wlr_keyboard_modifiers *modifiers)
}
WInputMethodHelper o-- WInputMethodHelperPrivate : d_ptr
WInputMethodHelperPrivate ..> GrabHandlerArg : uses
WInputMethodHelperPrivate ..> WSeat : holds
WInputMethodHelperPrivate ..> WInputMethodManagerV2 : manages
WInputMethodHelperPrivate ..> WTextInputManagerV1 : manages
WInputMethodHelperPrivate ..> WTextInputManagerV2 : manages
WInputMethodHelperPrivate ..> WTextInputManagerV3 : manages
GrabHandlerArg ..> qw_input_method_keyboard_grab_v2 : grab
class wlr_seat_keyboard_grab {
+void* data
+wlr_seat* seat
}
class wlr_keyboard_modifiers
wlr_seat_keyboard_grab --> GrabHandlerArg : data cast
WSeat ..> wlr_seat_keyboard_grab : keyboardGrab hooks
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
80320ce to
9d04804
Compare
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In InputDevice::initDevice the lambda connected to TreelandUserConfig signals captures a raw WInputDevice* without any disconnection or lifetime coordination, which risks calling libinput_device_handle on a freed device; consider tying the connection to the device’s lifetime (e.g., using the device as the receiver or disconnecting on device destruction).
- WVirtualInputHelper currently only filters virtual pointers by seat (shouldAcceptSeat) but not virtual keyboards; if multiple seats are ever used, you may want to apply the same seat check for virtual keyboards to avoid attaching keyboards to unintended seats.
- When WVirtualInputHelper is destroyed, attached virtual input devices remain on the seat and are only cleaned up via before_destroy, which may outlive the helper; consider whether you should explicitly detach or otherwise clean up virtualKeyboards/virtualPointers when the helper goes away to avoid dangling connections or unexpected behavior.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In InputDevice::initDevice the lambda connected to TreelandUserConfig signals captures a raw WInputDevice* without any disconnection or lifetime coordination, which risks calling libinput_device_handle on a freed device; consider tying the connection to the device’s lifetime (e.g., using the device as the receiver or disconnecting on device destruction).
- WVirtualInputHelper currently only filters virtual pointers by seat (shouldAcceptSeat) but not virtual keyboards; if multiple seats are ever used, you may want to apply the same seat check for virtual keyboards to avoid attaching keyboards to unintended seats.
- When WVirtualInputHelper is destroyed, attached virtual input devices remain on the seat and are only cleaned up via before_destroy, which may outlive the helper; consider whether you should explicitly detach or otherwise clean up virtualKeyboards/virtualPointers when the helper goes away to avoid dangling connections or unexpected behavior.
## Individual Comments
### Comment 1
<location path="src/input/inputdevice.cpp" line_range="368-377" />
<code_context>
- return false;
+
+ auto config = Helper::instance()->config();
+ auto applyAccelProfile = [device, config]() {
+ auto deviceType = device->qtDevice()->type();
+ if (deviceType == QInputDevice::DeviceType::Mouse ||
+ deviceType == QInputDevice::DeviceType::TouchPad) {
+ configAccelProfile(libinput_device_handle(device->handle()),
+ static_cast<libinput_config_accel_profile>(config->inputAccelProfile()));
+ }
+ };
+
+ connect(config, &TreelandUserConfig::inputAccelProfileChanged, this, applyAccelProfile);
+ connect(config, &TreelandUserConfig::configInitializeSucceed, this, applyAccelProfile);
}
</code_context>
<issue_to_address>
**issue (bug_risk):** Lifetime of `device` in the accel-profile lambda can outlive the actual input device
Because the `TreelandUserConfig` connection uses the global `InputDevice` singleton as the receiver and captures `device` by value, the lambda can be invoked after the physical device is destroyed, leaving `device` dangling and causing UB/crashes on later `inputAccelProfileChanged` / `configInitializeSucceed` signals.
Please update this so the connection is scoped to the device’s lifetime (e.g., use `device` as the receiver/context), or explicitly disconnect it in the device teardown path. Alternatively, capture only a safe/validated handle (e.g., `libinput_device*` or a weak/guarded reference) and check it before use. Note that with multiple devices this creates multiple long‑lived connections on the singleton, which worsens the issue.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
454be58 to
1b83b38
Compare
f1da01c to
b04f671
Compare
There was a problem hiding this comment.
Hey - I've found 5 issues, and left some high level feedback:
- In WVirtualInputHelper::maybeMapToOutput(), the WOutput::fromHandle(qw_output::from(output)) result is only used for its side‑effect as a validity check; consider making that explicit (e.g. via Q_ASSERT or a named local) so the intent is clearer and future refactors don’t accidentally remove it as “unused.”
- WVirtualPointerManagerV1 currently ignores the QObject *parent passed to its constructor and doesn’t forward it to QObject; if you intend to use QObject’s lifetime/parenting here, call QObject(parent) in the initializer list or drop the parent parameter to avoid confusion.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In WVirtualInputHelper::maybeMapToOutput(), the WOutput::fromHandle(qw_output::from(output)) result is only used for its side‑effect as a validity check; consider making that explicit (e.g. via Q_ASSERT or a named local) so the intent is clearer and future refactors don’t accidentally remove it as “unused.”
- WVirtualPointerManagerV1 currently ignores the QObject *parent passed to its constructor and doesn’t forward it to QObject; if you intend to use QObject’s lifetime/parenting here, call QObject(parent) in the initializer list or drop the parent parameter to avoid confusion.
## Individual Comments
### Comment 1
<location path="src/input/inputdevice.cpp" line_range="365-374" />
<code_context>
+ deviceType == QInputDevice::DeviceType::Mouse) {
+ auto config = Helper::instance()->config();
+ auto applyAccelProfile = [device, config]() {
+ auto deviceType = device->qtDevice()->type();
+ if (deviceType == QInputDevice::DeviceType::Mouse ||
+ deviceType == QInputDevice::DeviceType::TouchPad) {
+ configAccelProfile(libinput_device_handle(device->handle()),
+ static_cast<libinput_config_accel_profile>(config->inputAccelProfile()));
+ }
+ };
+
+ auto &connections = m_deviceConnections[device];
+ connections.append(connect(config, &TreelandUserConfig::inputAccelProfileChanged, this, applyAccelProfile));
+ connections.append(connect(config, &TreelandUserConfig::configInitializeSucceed, this, applyAccelProfile));
+ }
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** The accel profile is never applied immediately on device init and may miss if config is already initialized.
In `initDevice`, `applyAccelProfile` is only triggered by `inputAccelProfileChanged` and `configInitializeSucceed`. For devices added after config initialization, if the user never changes the accel profile, it’s never applied and the device stays on the libinput default. Please ensure the current accel profile is applied once when the device is initialized (after wiring the signals), or otherwise use the config state to apply the existing value immediately on attachment.
</issue_to_address>
### Comment 2
<location path="waylib/src/server/protocols/wvirtualinputhelper.cpp" line_range="111-120" />
<code_context>
+ d->seat->cursor()->handle()->map_input_to_output(device->handle()->handle(), output);
+}
+
+void WVirtualInputHelper::handleNewVKV1(::wlr_virtual_keyboard_v1 *virtualKeyboard)
+{
+ W_D(WVirtualInputHelper);
+ auto *device = ensureDevice(qw_input_device::from(&virtualKeyboard->keyboard.base));
+ if (!device || device->seat() || d->virtualKeyboards.contains(device))
+ return;
+
+ d->virtualKeyboards.append(device);
+ attachDevice(device);
+ device->safeConnect(&qw_input_device::before_destroy, this, [d, device] {
+ d->virtualKeyboards.removeOne(device);
+ device->safeDeleteLater();
</code_context>
<issue_to_address>
**issue (bug_risk):** Virtual keyboards are never detached from the seat on destruction.
In `handleNewVKV1`, the device is attached to the seat via `attachDevice`, but in the `before_destroy` handler you only remove it from `virtualKeyboards` and delete it. You never call `seat->detachInputDevice(device)`, unlike the previous `WInputMethodHelper::handleNewVKV1` implementation. This can leave the seat referencing a destroyed device. Please detach the device from `d->seat` here (guarded by a null check) before scheduling deletion.
</issue_to_address>
### Comment 3
<location path="waylib/src/server/protocols/wvirtualinputhelper.cpp" line_range="126-120" />
<code_context>
+ });
+}
+
+void WVirtualInputHelper::handleNewVPV1(::wlr_virtual_pointer_v1_new_pointer_event *event)
+{
+ W_D(WVirtualInputHelper);
+ if (!event || !shouldAcceptSeat(event->suggested_seat))
+ return;
+
+ auto *device = ensureDevice(qw_input_device::from(&event->new_pointer->pointer.base));
+ if (!device || device->seat() || d->virtualPointers.contains(device))
+ return;
+
+ d->virtualPointers.append(device);
+ attachDevice(device);
+ maybeMapToOutput(device, event->suggested_output);
+ device->safeConnect(&qw_input_device::before_destroy, this, [d, device] {
+ d->virtualPointers.removeOne(device);
+ device->safeDeleteLater();
</code_context>
<issue_to_address>
**issue (bug_risk):** Virtual pointers are also not detached from the seat when destroyed.
In `handleNewVPV1`, the `before_destroy` handler only updates `virtualPointers` and deletes the device; it never calls `seat->detachInputDevice(device)`. This can leave the seat referencing a destroyed device. As with the physical input path, detach the device from the seat before `safeDeleteLater()` to keep the lifecycle consistent and avoid stale references in `WSeat`.
</issue_to_address>
### Comment 4
<location path="src/input/inputdevice.cpp" line_range="357" />
<code_context>
}
-bool InputDevice::initTouchPad(WInputDevice *device)
+void InputDevice::initDevice(WInputDevice *device)
{
- if (device->handle()->is_libinput()
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying initDevice by removing manual connection bookkeeping and redundant device-type checks, relying instead on QObject-based auto-disconnection and a single type filter before creating the lambda.
You can simplify this by avoiding explicit connection bookkeeping and by removing the duplicated device-type logic.
### 1. Remove `m_deviceConnections` and `deinitDevice` using a context object
Instead of storing `QMetaObject::Connection`s and manually disconnecting, use a `QObject*` as the connection context so Qt auto‑disconnects when the context is destroyed. Since the lambda is per‑device, `device` is a natural context if `WInputDevice` derives from `QObject` (it almost certainly does, given the Qt pattern):
```cpp
void InputDevice::initDevice(WInputDevice *device)
{
if (!device || !device->handle() || !device->handle()->is_libinput()) {
return;
}
const auto deviceType = device->qtDevice()->type();
if (deviceType == QInputDevice::DeviceType::TouchPad) {
configTapEnabled(libinput_device_handle(device->handle()), LIBINPUT_CONFIG_TAP_ENABLED);
}
if (deviceType != QInputDevice::DeviceType::TouchPad &&
deviceType != QInputDevice::DeviceType::Mouse) {
return;
}
auto config = Helper::instance()->config();
auto applyAccelProfile = [device, config, deviceType]() {
// No need to re-check type; already filtered above and fixed per device.
configAccelProfile(libinput_device_handle(device->handle()),
static_cast<libinput_config_accel_profile>(config->inputAccelProfile()));
};
// Use `device` as context: signals auto-disconnect when `device` is destroyed.
connect(config, &TreelandUserConfig::inputAccelProfileChanged,
device, applyAccelProfile);
connect(config, &TreelandUserConfig::configInitializeSucceed,
device, applyAccelProfile);
}
```
With this pattern:
- `m_deviceConnections` can be removed.
- `deinitDevice` can be either removed (if not used outside) or reduced to a no‑op, because disconnection is handled automatically by Qt when `device` is destroyed.
If you need `deinitDevice` API to stay, you can keep an empty implementation:
```cpp
void InputDevice::deinitDevice(WInputDevice *device)
{
Q_UNUSED(device);
// No manual disconnect needed; connections use `device` as context.
}
```
### 2. Avoid repeated type checks inside the lambda
The lambda currently re-checks `device->qtDevice()->type()`. Since you already filter by type in `initDevice`, you can capture the `deviceType` or simply rely on the earlier guard. This reduces cognitive load without changing behavior (the outer `if` ensures only mouse/touchpad devices get these connections). The example above uses `deviceType` captured in the lambda.
</issue_to_address>
### Comment 5
<location path="src/input/inputdevice.h" line_range="68" />
<code_context>
static InputDevice *m_instance;
std::unique_ptr<GestureRecognizer> m_touchpadRecognizer;
+ QHash<WInputDevice *, QVector<QMetaObject::Connection>> m_deviceConnections;
uint m_touchpadFingerCount = 0;
};
</code_context>
<issue_to_address>
**issue (complexity):** Consider relying on Qt’s built-in connection management instead of tracking per-device QMetaObject::Connection objects in m_deviceConnections.
You can avoid the extra lifecycle bookkeeping (`m_deviceConnections`) by using Qt’s existing connection management, which simplifies `InputDevice`’s state and API surface without changing behavior.
Instead of storing `QMetaObject::Connection` objects per `WInputDevice*`, you can:
1. Use the device (or a small per-device helper `QObject`) as the connection context.
2. Rely on `QObject::disconnect` by sender/receiver in `deinitDevice()` rather than tracking individual connections.
### Example: use device as connection context
In the cpp, where you currently do something like:
```cpp
auto c1 = connect(device, &WInputDevice::accelProfileChanged,
this, &InputDevice::onAccelProfileChanged);
auto c2 = connect(device, &WInputDevice::somethingElse,
this, &InputDevice::onSomethingElse);
m_deviceConnections[device].append(c1);
m_deviceConnections[device].append(c2);
```
You can instead write:
```cpp
connect(device, &WInputDevice::accelProfileChanged,
this, &InputDevice::onAccelProfileChanged);
connect(device, &WInputDevice::somethingElse,
this, &InputDevice::onSomethingElse);
```
and drop the hash entirely. Then `deinitDevice()` only needs to disconnect by sender/receiver:
```cpp
void InputDevice::deinitDevice(WInputDevice *device)
{
// Remove all connections between this and `device`
QObject::disconnect(device, nullptr, this, nullptr);
// existing deinitialization logic...
}
```
If `device` is a `QObject` and will be destroyed at some point, Qt will additionally auto-disconnect on destruction, so you don’t need explicit bookkeeping for cleanup.
### If you need per-device isolation
If you want stricter separation, introduce a per-device helper instead of a global hash:
```cpp
class DeviceConnections : public QObject
{
Q_OBJECT
public:
DeviceConnections(WInputDevice *device, InputDevice *input, QObject *parent = nullptr)
: QObject(parent)
{
connect(device, &WInputDevice::accelProfileChanged,
input, &InputDevice::onAccelProfileChanged);
// other device-specific connections...
}
};
```
Then in `initDevice`:
```cpp
void InputDevice::initDevice(WInputDevice *device)
{
new DeviceConnections(device, this, device); // parented to device; auto-destroyed
}
```
With either approach, you can remove:
```cpp
QHash<WInputDevice *, QVector<QMetaObject::Connection>> m_deviceConnections;
```
from the header and avoid the cross-cutting connection bookkeeping, while keeping the new `initDevice` / `deinitDevice` API intact.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
a4bdeee to
bb508d0
Compare
Introduce WVirtualInputHelper to manage virtual input devices created via Wayland protocols. This moves virtual keyboard handling out of WInputMethodHelper and adds support for zwlr_virtual_pointer_v1. Virtual devices are automatically attached to the seat and cleaned up when destroyed. Log: add WVirtualInputHelper for virtual keyboard and pointer Influence: virtual input pointer and keyboard
Create the single-pixel-buffer-v1 manager during compositor initialization to support clients creating single pixel buffers. This protocol allows clients to efficiently represent solid color surfaces without allocating a full wl_buffer. Log: add single-pixel-buffer-v1 protocol support
Add libinput accel speed/profile dconfig options and wire them into input device setup. Apply settings on device hotplug and config changes, and reuse a helper to update all current devices. Log: apply libinput accel config Influence: pointer, touchpad, virtual input
These protocols are required for running the wl-find-cursor client,
which is used in automated testing to simulate input events.
Summary by Sourcery
Add support for virtual input devices and single-pixel buffer protocol, and integrate centralized input device initialization and configuration management.
New Features:
Enhancements:
Build: