Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 100 additions & 1 deletion waylib/src/server/protocols/wxwayland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

#include <wlr_all.h>

#include <wlr/util/box.h>

Check warning on line 20 in waylib/src/server/protocols/wxwayland.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <wlr/util/box.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <wlr/xwayland/xwayland.h>

Check warning on line 21 in waylib/src/server/protocols/wxwayland.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <wlr/xwayland/xwayland.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

// For wl_keyboard_send_modifiers
#include <wayland-server-protocol.h>

Check warning on line 24 in waylib/src/server/protocols/wxwayland.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <wayland-server-protocol.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <QCoreApplication>

Check warning on line 26 in waylib/src/server/protocols/wxwayland.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QCoreApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 27 in waylib/src/server/protocols/wxwayland.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <array>
#include <utility>
Expand Down Expand Up @@ -59,6 +62,15 @@

QMetaObject::Connection cursorPositionChangedConnection;

// Keyboard modifiers sync to the Xwayland server.
void syncModifiersToXWayland();
void watchSeatKeyboard();
void on_keyboard_device_destroy();

WScopedListener keyboardModifiersListener;
WScopedListener keyboardDeviceDestroyListener;
QMetaObject::Connection seatKeyboardChangedConnection;

// Async property reading
struct PerWindowProps
{
Expand Down Expand Up @@ -485,6 +497,89 @@
}
}

void WXWaylandPrivate::syncModifiersToXWayland()
{
W_Q(WXWayland);

auto *xwayland = q->handle();
if (!xwayland || !xwayland->server || !xwayland->server->client || !xwayland->seat)
return;

auto *seat = xwayland->seat;
auto *keyboard = wlr_seat_get_keyboard(seat);
if (!keyboard)
return;

// When Xwayland itself holds the keyboard focus, the seat already
// delivers the modifiers to it through the normal path.
auto *focusedClient = seat->keyboard_state.focused_client;
if (focusedClient && focusedClient->client == xwayland->server->client)
return;

auto *seatClient = wlr_seat_client_for_wl_client(seat, xwayland->server->client);
if (!seatClient || wl_list_empty(&seatClient->keyboards))
return;

// The Xwayland server applies every wl_keyboard.modifiers event to its
// XKB state (keyboard_handle_modifiers in xwayland-input.c) even when it
// doesn't hold the keyboard focus, so push the current state explicitly
// to keep the state seen by X11 APIs (e.g. XkbGetState) fresh.
const auto &mods = keyboard->modifiers;
uint32_t serial = wlr_seat_client_next_serial(seatClient);
wl_resource *resource;
wl_resource_for_each(resource, &seatClient->keyboards) {
wl_keyboard_send_modifiers(resource, serial,
mods.depressed, mods.latched,
mods.locked, mods.group);
}
}

void WXWaylandPrivate::watchSeatKeyboard()
{
W_Q(WXWayland);

keyboardModifiersListener.disconnect();
keyboardDeviceDestroyListener.disconnect();

// setSeat() may switch to another seat (or clear it), so rebind the
// keyboardChanged connection to the current seat on every call.
if (seatKeyboardChangedConnection) {
QObject::disconnect(seatKeyboardChangedConnection);
seatKeyboardChangedConnection = {};
}

auto *xwayland = q->handle();
auto *seat = xwayland ? xwayland->seat : nullptr;
if (!seat)
return;

// The seat keyboard can be replaced (e.g. by the input method's virtual
// keyboard) or destroyed at any time, so re-attach on changes.
if (auto *seatObject = WSeat::fromHandle(seat)) {
seatKeyboardChangedConnection = QObject::connect(seatObject,
&WSeat::keyboardChanged,
q, [this] {
watchSeatKeyboard();
syncModifiersToXWayland();
});
}

if (auto *keyboard = wlr_seat_get_keyboard(seat)) {
keyboardModifiersListener.init(&keyboard->events.modifiers,
this, &WXWaylandPrivate::syncModifiersToXWayland);
keyboardDeviceDestroyListener.init(&keyboard->base.events.destroy,
this, &WXWaylandPrivate::on_keyboard_device_destroy);
}
}

void WXWaylandPrivate::on_keyboard_device_destroy()
{
// The keyboard is going away; detach our listeners from it before the
// wlr_keyboard memory is freed.
keyboardModifiersListener.disconnect();
keyboardDeviceDestroyListener.disconnect();
}

void WXWayland::setSeat(WSeat *seat)
{
W_D(WXWayland);
Expand All @@ -494,6 +589,8 @@

d->watchSeatCursor();
d->syncCursorPositionToXWayland();
d->watchSeatKeyboard();
d->syncModifiersToXWayland();
Comment on lines +585 to +593

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the connection is now disconnected and reset on every watchSeatKeyboard() call (i.e. on every setSeat() change) before being rebound to the current seat. Current head: 45e4d6a (rebased onto master).

}

WSeat *WXWayland::seat() const
Expand Down Expand Up @@ -612,9 +709,11 @@
listeners()->add(&handle->events.ready, this, [this, d] (void *) {
d->init();
// The seat may be set before the Xwayland server starts; sync the
// current cursor position once the server client is available.
// current cursor position and keyboard state once the server client
// is available.
d->watchSeatCursor();
d->syncCursorPositionToXWayland();
d->syncModifiersToXWayland();
Q_EMIT ready();
});

Expand Down
Loading