Skip to content
Merged
Show file tree
Hide file tree
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
72 changes: 72 additions & 0 deletions src/seat/helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,48 @@ void Helper::onShowDesktop()
surface->startShowDesktopAnimation(false);
}
}

if (s == WindowManagementInterfaceV1::DesktopState::Show) {
// Find the desktop background surface first
SurfaceWrapper *desktopSurface = nullptr;
const auto &backgroundSurfaces = m_shellHandler->m_backgroundContainer->surfaces();
for (SurfaceWrapper *w : backgroundSurfaces) {
auto *layer = qobject_cast<WLayerSurface *>(w->shellSurface());
if (layer && layer->scope() == QStringLiteral("dde-shell/desktop")) {
desktopSurface = w;
break;
}
}

const auto &seats = m_seatManager->seats();
for (auto *seat : seats) {
auto *seatContainer = m_rootSurfaceContainer->getSeatContainer(seat);
// Seat may be in transition (hotplug); requestKeyboardFocus asserts
// on a missing seat container.
if (!seatContainer)
continue;

// Dismiss any popup keyboard grab: the grab would redirect keyboard
// focus back to the popup and defeat the desktop-surface transfer.
seatContainer->dismissPopups();
// Move keyboard focus to the desktop surface (or drop it when the
// desktop surface is unavailable) so layer-shell windows that close
// on focus loss exit.
requestKeyboardFocus(desktopSurface, Qt::OtherFocusReason, seat);
}
} else if (s == WindowManagementInterfaceV1::DesktopState::Normal) {
// m_showDesktop already set to s above; the protocol state is already Normal.
restoreShowDesktopFocus();
}
}

void Helper::restoreShowDesktopFocus()
{
const auto &seats = m_seatManager->seats();
for (auto *seat : seats) {
if (auto *seatContainer = m_rootSurfaceContainer->getSeatContainer(seat))
seatContainer->restoreShowDesktopFocus();
}
}

void Helper::onSetCopyOutput(VirtualOutputInterfaceV1 *interface)
Expand Down Expand Up @@ -2421,6 +2463,27 @@ bool Helper::beforeDisposeEvent(WSeat *seat, QWindow *targetWindow, QInputEvent
m_currentEventSeat = targetSeat;
[[maybe_unused]] auto clearEventSeat = qScopeGuard([this] { m_currentEventSeat = nullptr; });

// Dismiss the popup grab when the user presses a button outside the popup
// (e.g. on the desktop or another client). wlroots' xdg popup keyboard grab
// redirects keyboard focus back to the popup, so it must be ended explicitly.
if (event->type() == QEvent::MouseButtonPress) {
if (auto *seatContainer = m_rootSurfaceContainer->getSeatContainer(targetSeat)) {
if (seatContainer->hasPopupGrab()) {
auto *focused = targetSeat->handle()->pointer_state.focused_surface;
bool clickOnPopup = false;
if (focused) {
if (auto *wSurface = WSurface::fromHandle(focused)) {
if (auto *wrapper = m_rootSurfaceContainer->getSurface(wSurface))
clickOnPopup = (wrapper->type() == SurfaceWrapper::Type::XdgPopup);
}
}
// seat->drag is non-null during an active DnD drag, which also
// installs a keyboard grab; never end that grab here.
if (!clickOnPopup && !targetSeat->handle()->drag)
seatContainer->dismissPopups();
}
}
}
if (seat == m_primarySeat) {
if (event->type() == QEvent::KeyPress) {
auto kevent = static_cast<QKeyEvent *>(event);
Expand Down Expand Up @@ -2855,6 +2918,8 @@ void Helper::setActivatedSurface(SurfaceWrapper *newActivateSurface, WSeat *seat
if (oldPrimarySurface)
oldPrimarySurface->setActivate(false);

bool wasShowingDesktop = false;

if (newActivateSurface) {
Q_ASSERT(newActivateSurface->showOnWorkspace(workspace()->current()->id()));
newActivateSurface->stackToLast();
Expand All @@ -2871,6 +2936,7 @@ void Helper::setActivatedSurface(SurfaceWrapper *newActivateSurface, WSeat *seat
m_showDesktop = WindowManagementInterfaceV1::DesktopState::Normal;
m_windowManagementInterfaceV1->setDesktopState(WindowManagementInterfaceV1::DesktopState::Normal);
newActivateSurface->setHideByShowDesk(true);
wasShowingDesktop = true;
}

Q_ASSERT(newActivateSurface->hasActiveCapability());
Expand All @@ -2881,6 +2947,11 @@ void Helper::setActivatedSurface(SurfaceWrapper *newActivateSurface, WSeat *seat
// it for the primary seat. Do not emit Helper::activatedSurfaceChanged again here.
seatContainer->setActivatedSurface(newActivateSurface, Qt::OtherFocusReason);

// This also restores keyboard focus on the other seats; the caller's subsequent
// requestKeyboardFocus() only covers the target seat.
if (wasShowingDesktop)
restoreShowDesktopFocus();

if (isPrimarySeat && newActivateSurface) {
Q_ASSERT(newActivateSurface->hasActiveCapability());
newActivateSurface->setActivate(true);
Expand Down Expand Up @@ -3425,6 +3496,7 @@ void Helper::restoreFromShowDesktop(SurfaceWrapper *activeSurface)
surface->setSurfaceState(SurfaceWrapper::State::Minimized);
}
}
restoreShowDesktopFocus();
}
}

Expand Down
1 change: 1 addition & 0 deletions src/seat/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ private Q_SLOTS:
bool isNvidiaCardPresent();
void setWorkspaceVisible(bool visible);
void restoreFromShowDesktop(SurfaceWrapper *activeSurface = nullptr);
void restoreShowDesktopFocus();
void setNoAnimation(bool noAnimation);

void updateSurfaceSeatInteraction(SurfaceWrapper *surface, WSeat *seat);
Expand Down
41 changes: 26 additions & 15 deletions src/surface/seatsurfacemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
if (!helper)
return;

// While showing the desktop, keyboard focus is on the desktop layer, not on the
// (hidden) activated surface; a focus-capability change of the activated
// surface must not yank keyboard focus back to the window.
if (helper->showDesktopState() == WindowManagementInterfaceV1::DesktopState::Show)
return;

if (m_activatedSurface->hasFocusCapability()) {
helper->requestKeyboardFocus(m_activatedSurface, Qt::ActiveWindowFocusReason, m_seat);
} else {
Expand Down Expand Up @@ -159,7 +165,20 @@
}
}

void SeatSurfaceManager::restoreShowDesktopFocus()

Check warning on line 168 in src/surface/seatsurfacemanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'restoreShowDesktopFocus' is never used.
{
if (!m_activatedSurface || !m_activatedSurface->hasFocusCapability())
return;

// The seat may still be in transition (hotplug): without a container the
// subsequent keyboard focus request would assert.
if (!m_rootContainer || !m_rootContainer->getSeatContainer(m_seat))
return;

setKeyboardFocusSurface(m_activatedSurface, Qt::OtherFocusReason);
}

void SeatSurfaceManager::beginMoveResize(SurfaceWrapper *surface, Qt::Edges edges)

Check warning on line 181 in src/surface/seatsurfacemanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'beginMoveResize' is never used.
{
if (m_moveResizeState.surface)
endMoveResize();
Expand Down Expand Up @@ -425,21 +444,6 @@
}
}

// Detect DnD drag keyboard grab:
// In wlr_seat_start_drag(), drag->keyboard_grab.data = drag is set before
// wlr_seat_keyboard_start_grab() is called (which emits keyboard_grab_begin),
// but seat->drag = drag is set AFTER the grab begins. So seat->drag is still
// nullptr when this signal handler runs. Instead, cast grab->data to a
// wlr_drag pointer and validate via grab_type (enum values 0-2 are safe;
// an xdg_popup_grab's client pointer will never equal those small integers).
if (grab->data) {
auto *possibleDrag = static_cast<struct wlr_drag *>(grab->data);
if (possibleDrag->grab_type <= WLR_DRAG_GRAB_KEYBOARD_TOUCH) {
qCDebug(lcTlPopupFocus) << "Drag keyboard grab started (not popup)";
return;
}
}

m_hasPopupGrab = true;
qCDebug(lcTlPopupFocus) << "Popup keyboard grab started";
}
Expand All @@ -454,6 +458,13 @@
qCDebug(lcTlPopupFocus) << "Popup keyboard grab ended, restoring focus to:"
<< m_activatedSurface;

// While showing the desktop, keyboard focus is on the desktop layer, not on the
// (hidden) activated surface; do not yank it back to the window.
if (auto *helper = Helper::instance()) {
if (helper->showDesktopState() == WindowManagementInterfaceV1::DesktopState::Show)
return;
}

if (m_activatedSurface && m_activatedSurface->hasFocusCapability()) {
setKeyboardFocusSurface(m_activatedSurface, Qt::ActiveWindowFocusReason);
}
Expand Down
3 changes: 3 additions & 0 deletions src/surface/seatsurfacemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
SurfaceWrapper *keyboardFocusSurface() const { return m_keyboardFocusSurface; }
void setKeyboardFocusSurface(SurfaceWrapper *surface, Qt::FocusReason reason = Qt::OtherFocusReason);

void restoreShowDesktopFocus();

struct MoveResizeState {
SurfaceWrapper *surface = nullptr; ///< The surface being moved/resized
Qt::Edges edges = Qt::Edges(); ///< Resize edges (empty for move)
Expand Down Expand Up @@ -62,6 +64,7 @@
// Popup keyboard grab management
void givePopupFocus(SurfaceWrapper *popupWrapper);
void dismissPopups();
bool hasPopupGrab() const { return m_hasPopupGrab; }

Check warning on line 67 in src/surface/seatsurfacemanager.h

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'hasPopupGrab' is never used.

Q_SIGNALS:
void activatedSurfaceChanged(SurfaceWrapper *surface);
Expand Down
Loading