diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -875,7 +875,7 @@ void skipPagerChanged(); void skipSwitcherChanged(); void iconChanged(); - void activeChanged(); + void activeChanged(bool active); void keepAboveChanged(bool); void keepBelowChanged(bool); /** diff --git a/abstract_client.cpp b/abstract_client.cpp --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -38,6 +38,8 @@ #include "wayland_server.h" #include +#include +#include #include @@ -226,7 +228,7 @@ workspace()->updateClientLayer(*it); doSetActive(); - emit activeChanged(); + emit activeChanged(m_active); updateMouseGrab(); } @@ -1392,7 +1394,7 @@ ); connect(this, &AbstractClient::captionChanged, w, [w, this] { w->setTitle(caption()); }); - connect(this, &AbstractClient::activeChanged, w, [w, this] { w->setActive(isActive()); }); + connect(this, &AbstractClient::activeChanged, w, &PlasmaWindowInterface::setActive); connect(this, &AbstractClient::fullScreenChanged, w, [w, this] { w->setFullscreen(isFullScreen()); }); connect(this, &AbstractClient::keepAboveChanged, w, &PlasmaWindowInterface::setKeepAbove); connect(this, &AbstractClient::keepBelowChanged, w, &PlasmaWindowInterface::setKeepBelow); diff --git a/virtualkeyboard.h b/virtualkeyboard.h --- a/virtualkeyboard.h +++ b/virtualkeyboard.h @@ -57,10 +57,12 @@ void setEnabled(bool enable); void updateSni(); void updateInputPanelState(); + void adoptInputMethodContext(); bool m_enabled = false; KStatusNotifierItem *m_sni = nullptr; QScopedPointer m_inputWindow; + QPointer m_inputSurface; QPointer m_trackedClient; // If a surface loses focus immediately after being resized by the keyboard, don't react to it to avoid resize loops QTimer *m_floodTimer; diff --git a/virtualkeyboard.cpp b/virtualkeyboard.cpp --- a/virtualkeyboard.cpp +++ b/virtualkeyboard.cpp @@ -28,7 +28,9 @@ #include "xkb.h" #include "screenlockerwatcher.h" +#include #include +#include #include #include #include @@ -126,6 +128,16 @@ t->create(); auto t2 = waylandServer()->display()->createTextInputManager(TextInputInterfaceVersion::UnstableV2, waylandServer()->display()); t2->create(); + + connect(waylandServer()->inputPanel(), &InputPanelInterface::inputPanelSurfaceAdded, this, [this](InputPanelSurfaceInterface *surface) { + m_inputSurface = waylandServer()->createInputPanelClient(surface); + connect(m_inputSurface, &KWin::AbstractClient::frameGeometryChanged, this, [this] { + if (!m_trackedClient) + return; + m_trackedClient->setVirtualKeyboardGeometry(m_inputSurface->rect()); + }); + }); + connect(waylandServer()->seat(), &SeatInterface::focusedTextInputChanged, this, [this] { disconnect(m_waylandShowConnection); @@ -149,11 +161,15 @@ } ); m_waylandResetConnection = connect(t, &TextInputInterface::requestReset, qApp->inputMethod(), &QInputMethod::reset); - m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, - [] { - qApp->inputMethod()->update(Qt::ImQueryAll); - } - ); + m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, [t, this] { + if (t->isEnabled()) { + waylandServer()->inputMethod()->sendDeactivate(); + waylandServer()->inputMethod()->sendActivate(); + adoptInputMethodContext(); + } else + waylandServer()->inputMethod()->sendDeactivate(); + qApp->inputMethod()->update(Qt::ImQueryAll); + }); auto newClient = waylandServer()->findClient(waylandServer()->seat()->focusedTextInputSurface()); // Reset the old client virtual keybaord geom if necessary @@ -222,6 +238,69 @@ QDBusConnection::sessionBus().asyncCall(msg); } +void VirtualKeyboard::adoptInputMethodContext() +{ + auto inputContext = waylandServer()->inputMethod()->context(); + TextInputInterface *ti = waylandServer()->seat()->focusedTextInput(); + + inputContext->sendSurroundingText(QString::fromUtf8(ti->surroundingText()), ti->surroundingTextCursorPosition(), ti->surroundingTextSelectionAnchor()); + inputContext->sendPreferredLanguage(QString::fromUtf8(ti->preferredLanguage())); + + connect(inputContext, &KWayland::Server::InputMethodContextInterface::keysym, waylandServer(), [](quint32 serial, quint32 time, quint32 sym, bool pressed, Qt::KeyboardModifiers modifiers) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + if (pressed) { + t->keysymPressed(sym, modifiers); + } else { + t->keysymReleased(sym, modifiers); + } + } + }); + + connect(inputContext, &KWayland::Server::InputMethodContextInterface::commitString, waylandServer(), [](qint32 serial, const QString &text) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->commit(text.toUtf8()); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::preeditCursor, waylandServer(), [](qint32 index) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->setPreEditCursor(index); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::preeditString, waylandServer(), [](uint32_t serial, const QString &text, const QString &commit) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->preEdit(text.toUtf8(), commit.toUtf8()); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::deleteSurroundingText, waylandServer(), [](int32_t index, uint32_t length) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->deleteSurroundingText(index, length); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::cursorPosition, waylandServer(), [](qint32 index, qint32 anchor) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->setCursorPosition(index, anchor); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::language, waylandServer(), [](uint32_t serial, const QString &language) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->setLanguage(language.toUtf8()); + } + }); + connect(inputContext, &KWayland::Server::InputMethodContextInterface::textDirection, waylandServer(), [](uint32_t serial, Qt::LayoutDirection direction) { + auto t = waylandServer()->seat()->focusedTextInput(); + if (t && t->isEnabled()) { + t->setTextDirection(direction); + } + }); +} + void VirtualKeyboard::updateSni() { if (!m_sni) { @@ -245,35 +324,41 @@ auto t = waylandServer()->seat()->focusedTextInput(); - if (!t || !m_inputWindow) { + if (!t) { return; } - const bool inputPanelHasBeenClosed = m_inputWindow->isVisible() && !qApp->inputMethod()->isVisible(); - if (inputPanelHasBeenClosed && m_floodTimer->isActive()) { - return; - } - m_floodTimer->start(); + if (m_inputWindow) { + const bool inputPanelHasBeenClosed = m_inputWindow->isVisible() && !qApp->inputMethod()->isVisible(); + if (inputPanelHasBeenClosed && m_floodTimer->isActive()) { + return; + } + m_floodTimer->start(); - m_inputWindow->setVisible(qApp->inputMethod()->isVisible()); + m_inputWindow->setVisible(qApp->inputMethod()->isVisible()); - if (qApp->inputMethod()->isVisible()) { - m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); - } + if (qApp->inputMethod()->isVisible()) { + m_inputWindow->setMask(m_inputWindow->rootObject()->childrenRect().toRect()); + } - if (m_inputWindow->isVisible() && m_trackedClient && m_inputWindow->rootObject()) { - const QRect inputPanelGeom = m_inputWindow->rootObject()->childrenRect().toRect().translated(m_inputWindow->geometry().topLeft()); + if (m_inputWindow->isVisible() && m_trackedClient && m_inputWindow->rootObject()) { + const QRect inputPanelGeom = m_inputWindow->rootObject()->childrenRect().toRect().translated(m_inputWindow->geometry().topLeft()); - m_trackedClient->setVirtualKeyboardGeometry(inputPanelGeom); + m_trackedClient->setVirtualKeyboardGeometry(inputPanelGeom); - t->setInputPanelState(true, QRect(0, 0, 0, 0)); + t->setInputPanelState(true, QRect(0, 0, 0, 0)); - } else { - if (inputPanelHasBeenClosed && m_trackedClient) { - m_trackedClient->setVirtualKeyboardGeometry(QRect()); - } + } else { + if (inputPanelHasBeenClosed && m_trackedClient) { + m_trackedClient->setVirtualKeyboardGeometry(QRect()); + } - t->setInputPanelState(false, QRect(0, 0, 0, 0)); + t->setInputPanelState(false, QRect(0, 0, 0, 0)); + } + } + if (m_inputSurface) { + m_trackedClient->setVirtualKeyboardGeometry(m_inputSurface->frameGeometry()); + t->setInputPanelState(true, QRect(0, 0, 0, 0)); } } diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -49,6 +49,9 @@ class Display; class DataDeviceInterface; class IdleInterface; +class InputMethodInterface; +class InputPanelInterface; +class InputPanelSurfaceInterface; class SeatInterface; class DataDeviceManagerInterface; class ServerSideDecorationManagerInterface; @@ -126,6 +129,12 @@ return m_xdgOutputManager; } KWayland::Server::LinuxDmabufUnstableV1Interface *linuxDmabuf(); + KWayland::Server::InputMethodInterface *inputMethod() const { + return m_inputMethod; + } + KWayland::Server::InputPanelInterface *inputPanel() const { + return m_inputPanel; + } QList clients() const { return m_clients; @@ -233,6 +242,8 @@ void removeLinuxDmabufBuffer(KWayland::Server::LinuxDmabufUnstableV1Buffer *buffer) { m_linuxDmabufBuffers.remove(buffer); } + AbstractClient * + createInputPanelClient(KWayland::Server::InputPanelSurfaceInterface *surface); Q_SIGNALS: void shellClientAdded(KWin::AbstractClient *); @@ -248,6 +259,8 @@ template void createSurface(T *surface); void initScreenLocker(); + void addClient(AbstractClient* client); + KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositor = nullptr; KWayland::Server::SeatInterface *m_seat = nullptr; @@ -265,6 +278,8 @@ KWayland::Server::XdgOutputManagerInterface *m_xdgOutputManager = nullptr; KWayland::Server::XdgDecorationManagerInterface *m_xdgDecorationManager = nullptr; KWayland::Server::LinuxDmabufUnstableV1Interface *m_linuxDmabuf = nullptr; + KWayland::Server::InputMethodInterface* m_inputMethod = nullptr; + KWayland::Server::InputPanelInterface* m_inputPanel = nullptr; QSet m_linuxDmabufBuffers; struct { KWayland::Server::ClientConnection *client = nullptr; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -25,6 +25,8 @@ #include "screens.h" #include "xdgshellclient.h" #include "workspace.h" +#include "internal_client.h" +#include "deleted.h" // Client #include @@ -66,6 +68,7 @@ #include #include #include +#include // KF #include @@ -144,6 +147,16 @@ } } +void WaylandServer::addClient(AbstractClient* client) +{ + m_clients << client; + if (client->readyForPainting()) { + emit shellClientAdded(client); + } else { + connect(client, &AbstractClient::windowShown, this, &WaylandServer::shellClientShown); + } +} + template void WaylandServer::createSurface(T *surface) { @@ -177,19 +190,82 @@ if (auto palette = m_paletteManager->paletteForSurface(surface->surface())) { client->installPalette(palette); } - m_clients << client; - if (client->readyForPainting()) { - emit shellClientAdded(client); - } else { - connect(client, &XdgShellClient::windowShown, this, &WaylandServer::shellClientShown); - } + addClient(client); //not directly connected as the connection is tied to client instead of this connect(m_XdgForeign, &KWayland::Server::XdgForeignInterface::transientChanged, client, [this](KWayland::Server::SurfaceInterface *child) { emit foreignTransientChanged(child); }); } +class InputPanelClient : public WaylandClient +{ +public: + InputPanelClient(KWayland::Server::InputPanelSurfaceInterface *panelSurface) + : WaylandClient(panelSurface->surface()) + , m_panelSurface(panelSurface) + { + init(); + setSkipPager(true); + setSkipTaskbar(true); + setKeepAbove(true); + setNoBorder(true); + + setObjectName(QStringLiteral("Input Panel")); + + connect(panelSurface, &QObject::destroyed, this, &WaylandClient::destroyClient); + connect(panelSurface, &KWayland::Server::InputPanelSurfaceInterface::topLevel, this, &InputPanelClient::showTopLevel); + connect(panelSurface, &KWayland::Server::InputPanelSurfaceInterface::overlayPanel, this, &InputPanelClient::showOverlayPanel); + } + +private: + void showTopLevel(OutputInterface *output, KWayland::Server::InputPanelSurfaceInterface::Position position) { + const QSize panelSize = m_panelSurface->surface()->size(); + QRect geo(output->globalPosition(), panelSize); + if (position == KWayland::Server::InputPanelSurfaceInterface::CenterBottom) { + geo.translate((output->pixelSize().width() - panelSize.width())/2, output->pixelSize().height() - panelSize.height()); + } + ensureInitialized(); + setFrameGeometry(geo); + } + + void showOverlayPanel() + { + auto focusedField = waylandServer()->findClient(waylandServer()->seat()->focusedTextInputSurface()); + if (focusedField) { + ensureInitialized(); + setFrameGeometry({focusedField->pos(), m_panelSurface->surface()->size()}); + } + } + + /// for some reason surfaces don't always emit ::committed + void ensureInitialized() { + if (!m_isInitialized) { + finishInit(); + } + } + + bool isCloseable() const override { return false; } + bool noBorder() const override { return true; } + bool isResizable() const override { return false; } + bool isMovable() const override { return false; } + bool isMovableAcrossScreens() const override { return false; } + bool userCanSetNoBorder() const override { return false; } + bool acceptsFocus() const override { return false; } + void showOnScreenEdge() override {} + bool supportsWindowRules() const override { return false; } + void closeWindow() override {} + + KWayland::Server::InputPanelSurfaceInterface *const m_panelSurface; +}; + +AbstractClient *WaylandServer::createInputPanelClient(KWayland::Server::InputPanelSurfaceInterface *surface) +{ + auto *client = new InputPanelClient(surface); + addClient(client); + return client; +} + class KWinDisplay : public KWayland::Server::FilteredDisplay { public: @@ -448,6 +524,9 @@ m_keyState = m_display->createKeyStateInterface(m_display); m_keyState->create(); + m_inputMethod = m_display->createInputMethodInterface(m_display); + m_inputPanel = m_display->createInputPanelInterface(m_display); + return true; } @@ -467,12 +546,12 @@ void WaylandServer::shellClientShown(Toplevel *t) { - XdgShellClient *c = dynamic_cast(t); + WaylandClient *c = dynamic_cast(t); if (!c) { - qCWarning(KWIN_CORE) << "Failed to cast a Toplevel which is supposed to be a XdgShellClient to XdgShellClient"; + qCWarning(KWIN_CORE) << "Failed to cast a Toplevel which is supposed to be a WaylandClient to WaylandClient"; return; } - disconnect(c, &XdgShellClient::windowShown, this, &WaylandServer::shellClientShown); + disconnect(c, &WaylandClient::windowShown, this, &WaylandServer::shellClientShown); emit shellClientAdded(c); } diff --git a/xdgshellclient.h b/xdgshellclient.h --- a/xdgshellclient.h +++ b/xdgshellclient.h @@ -25,6 +25,7 @@ #include "abstract_client.h" #include +#include namespace KWayland { @@ -49,22 +50,17 @@ FocusWindow }; -class KWIN_EXPORT XdgShellClient : public AbstractClient +class KWIN_EXPORT WaylandClient : public AbstractClient { Q_OBJECT - public: - XdgShellClient(KWayland::Server::XdgShellSurfaceInterface *surface); - XdgShellClient(KWayland::Server::XdgShellPopupInterface *surface); - ~XdgShellClient() override; + explicit WaylandClient(KWayland::Server::SurfaceInterface *surface); QRect inputGeometry() const override; QRect bufferGeometry() const override; QStringList activities() const override; QPoint clientContentPos() const override; QSize clientSize() const override; - QSize minSize() const override; - QSize maxSize() const override; QRect transparentRect() const override; NET::WindowType windowType(bool direct = false, int supported_types = 0) const override; void debug(QDebug &stream) const override; @@ -74,75 +70,49 @@ void blockActivityUpdates(bool b = true) override; QString captionNormal() const override; QString captionSuffix() const override; - void closeWindow() override; AbstractClient *findModal(bool allow_itself = false) override; - bool isCloseable() const override; bool isFullScreenable() const override; - bool isFullScreen() const override; bool isMaximizable() const override; - bool isMinimizable() const override; - bool isMovable() const override; - bool isMovableAcrossScreens() const override; - bool isResizable() const override; + bool isFullScreen() const override; bool isShown(bool shaded_is_shown) const override; bool isHiddenInternal() const override; void hideClient(bool hide) override; MaximizeMode maximizeMode() const override; MaximizeMode requestedMaximizeMode() const override; - bool noBorder() const override; void setFullScreen(bool set, bool user = true) override; void setNoBorder(bool set) override; void updateDecoration(bool check_workspace_pos, bool force = false) override; void setOnAllActivities(bool set) override; void takeFocus() override; - bool userCanSetFullScreen() const override; - bool userCanSetNoBorder() const override; bool wantsInput() const override; - bool dockWantsInput() const override; using AbstractClient::resizeWithChecks; void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet) override; using AbstractClient::setFrameGeometry; void setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet) override; - bool hasStrut() const override; quint32 windowId() const override; pid_t pid() const override; bool isLockScreen() const override; bool isInputMethod() const override; - bool isInitialPositionSet() const override; bool isTransient() const override; - bool hasTransientPlacementHint() const override; - QRect transientPlacement(const QRect &bounds) const override; QMatrix4x4 inputTransformation() const override; - void showOnScreenEdge() override; bool hasPopupGrab() const override; - void popupDone() override; void updateColorScheme() override; - bool isPopupWindow() const override; void killWindow() override; bool isLocalhost() const override; - bool supportsWindowRules() const override; void destroyClient() override; - void installPlasmaShellSurface(KWayland::Server::PlasmaShellSurfaceInterface *surface); - void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); - void installAppMenu(KWayland::Server::AppMenuInterface *appmenu); - void installPalette(KWayland::Server::ServerSideDecorationPaletteInterface *palette); - void installXdgDecoration(KWayland::Server::XdgDecorationInterface *decoration); - protected: void addDamage(const QRegion &damage) override; bool belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const override; void doSetActive() override; bool belongsToDesktop() const override; - Layer layerForDock() const override; void changeMaximize(bool horizontal, bool vertical, bool adjust) override; void doResizeSync() override; - bool acceptsFocus() const override; void doMinimize() override; void updateCaption() override; void doMove(int x, int y) override; -private Q_SLOTS: +protected Q_SLOTS: void handleConfigureAcknowledged(quint32 serial); void handleTransientForChanged(); void handleWindowClassChanged(const QByteArray &windowClass); @@ -155,44 +125,65 @@ void handleFullScreenRequested(bool fullScreen, KWayland::Server::OutputInterface *output); void handleWindowMenuRequested(KWayland::Server::SeatInterface *seat, quint32 serial, const QPoint &surfacePos); void handleGrabRequested(KWayland::Server::SeatInterface *seat, quint32 serial); - void handlePingDelayed(quint32 serial); - void handlePingTimeout(quint32 serial); - void handlePongReceived(quint32 serial); void handleCommitted(); -private: - /** - * Called when the shell is created. - */ +protected: void init(); + virtual void initType() {} /** - * Called for the XDG case when the shell surface is committed to the surface. + * Called for the when the surface is committed to the surface. * At this point all initial properties should have been set by the client. */ void finishInit(); void createWindowId(); void updateIcon(); - bool shouldExposeToWindowManagement(); void updateClientOutputs(); KWayland::Server::XdgShellSurfaceInterface::States xdgSurfaceStates() const; - void updateShowOnScreenEdge(); void updateMaximizeMode(MaximizeMode maximizeMode); // called on surface commit and processes all m_pendingConfigureRequests up to m_lastAckedConfigureReqest void updatePendingGeometry(); - QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const; void requestGeometry(const QRect &rect); void doSetGeometry(const QRect &rect); void unmap(); void markAsMapped(); QRect determineBufferGeometry() const; - void ping(PingReason reason); - static void deleteClient(XdgShellClient *c); + static void deleteClient(WaylandClient *c); QRect adjustMoveGeometry(const QRect &rect) const; QRect adjustResizeGeometry(const QRect &rect) const; - KWayland::Server::XdgShellSurfaceInterface *m_xdgShellToplevel; - KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup; + virtual void aboutToActivate() {} + virtual void refreshDecoration() {} + virtual KWayland::Server::SurfaceInterface *findTransientSurface() { return nullptr; } + virtual bool shouldExposeToWindowManagement(); + + /** + * @returns the serial of the configure call + */ + virtual int configureResize(const QSize &size, const QRect &newGeometry) + { + Q_UNUSED(size); + Q_UNUSED(newGeometry); + return 0; + } + + class RequestGeometryBlocker { + public: + RequestGeometryBlocker(WaylandClient *client) + : m_client(client) + { + m_client->m_requestGeometryBlockCounter++; + } + ~RequestGeometryBlocker() + { + m_client->m_requestGeometryBlockCounter--; + if (m_client->m_requestGeometryBlockCounter == 0) { + m_client->requestGeometry(m_client->m_blockedRequestGeometry); + } + } + private: + WaylandClient *m_client; + }; QRect m_bufferGeometry; QRect m_windowGeometry; @@ -223,43 +214,87 @@ quint32 m_windowId = 0; bool m_unmapped = true; NET::WindowType m_windowType = NET::Normal; - QPointer m_plasmaShellSurface; - QPointer m_appMenuInterface; - QPointer m_paletteInterface; - KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr; - KWayland::Server::XdgDecorationInterface *m_xdgDecoration = nullptr; + bool m_userNoBorder = false; bool m_fullScreen = false; bool m_transient = false; bool m_hidden = false; bool m_hasPopupGrab = false; qreal m_opacity = 1.0; - class RequestGeometryBlocker { //TODO rename ConfigureBlocker when this class is Xdg only - public: - RequestGeometryBlocker(XdgShellClient *client) - : m_client(client) - { - m_client->m_requestGeometryBlockCounter++; - } - ~RequestGeometryBlocker() - { - m_client->m_requestGeometryBlockCounter--; - if (m_client->m_requestGeometryBlockCounter == 0) { - m_client->requestGeometry(m_client->m_blockedRequestGeometry); - } - } - private: - XdgShellClient *m_client; - }; - friend class RequestGeometryBlocker; int m_requestGeometryBlockCounter = 0; QRect m_blockedRequestGeometry; + bool m_isInitialized = false; + QString m_caption; QString m_captionSuffix; - QHash m_pingSerials; +}; - bool m_isInitialized = false; +class KWIN_EXPORT XdgShellClient : public WaylandClient +{ + Q_OBJECT + +public: + explicit XdgShellClient(KWayland::Server::XdgShellSurfaceInterface *surface); + explicit XdgShellClient(KWayland::Server::XdgShellPopupInterface *surface); + ~XdgShellClient() override; + + void initType() override; + + void installPlasmaShellSurface(KWayland::Server::PlasmaShellSurfaceInterface *surface); + void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); + void installAppMenu(KWayland::Server::AppMenuInterface *appmenu); + void installPalette(KWayland::Server::ServerSideDecorationPaletteInterface *palette); + void installXdgDecoration(KWayland::Server::XdgDecorationInterface *decoration); + +private: + bool acceptsFocus() const override; + void closeWindow() override; + int configureResize(const QSize &size, const QRect &newGeometry) override; + void destroyClient() override; + bool dockWantsInput() const override; + bool hasStrut() const override; + bool hasTransientPlacementHint() const override; + bool isCloseable() const override; + bool isInitialPositionSet() const override; + bool isMinimizable() const override; + bool isMovable() const override; + bool isMovableAcrossScreens() const override; + bool isPopupWindow() const override; + bool isResizable() const override; + Layer layerForDock() const override; + QSize minSize() const override; + QSize maxSize() const override; + bool noBorder() const override; + void popupDone() override; + void showOnScreenEdge() override; + bool supportsWindowRules() const override; + void updateColorScheme() override; + bool userCanSetFullScreen() const override; + bool userCanSetNoBorder() const override; + + void aboutToActivate() override; + void refreshDecoration() override; + bool shouldExposeToWindowManagement() override; + KWayland::Server::SurfaceInterface *findTransientSurface() override; + QRect transientPlacement(const QRect &bounds) const override; + + void handlePingDelayed(quint32 serial); + void handlePingTimeout(quint32 serial); + void handlePongReceived(quint32 serial); + QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const; + void ping(PingReason reason); + void updateShowOnScreenEdge(); + + KWayland::Server::XdgShellSurfaceInterface *m_xdgShellToplevel; + KWayland::Server::XdgShellPopupInterface *m_xdgShellPopup; + + QPointer m_plasmaShellSurface; + QPointer m_appMenuInterface; + QPointer m_paletteInterface; + KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr; + KWayland::Server::XdgDecorationInterface *m_xdgDecoration = nullptr; + QHash m_pingSerials; friend class Workspace; }; diff --git a/xdgshellclient.cpp b/xdgshellclient.cpp --- a/xdgshellclient.cpp +++ b/xdgshellclient.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -53,38 +54,40 @@ #include #include -#include - #include +#include Q_DECLARE_METATYPE(NET::WindowType) using namespace KWayland::Server; namespace KWin { +WaylandClient::WaylandClient(KWayland::Server::SurfaceInterface *surface) +{ + setSurface(surface); +} + XdgShellClient::XdgShellClient(XdgShellSurfaceInterface *surface) - : AbstractClient() + : WaylandClient(surface->surface()) , m_xdgShellToplevel(surface) , m_xdgShellPopup(nullptr) { - setSurface(surface->surface()); init(); } XdgShellClient::XdgShellClient(XdgShellPopupInterface *surface) - : AbstractClient() + : WaylandClient(surface->surface()) , m_xdgShellToplevel(nullptr) , m_xdgShellPopup(surface) { - setSurface(surface->surface()); init(); } XdgShellClient::~XdgShellClient() = default; -void XdgShellClient::init() +void WaylandClient::init() { m_requestGeometryBlockCounter++; @@ -101,12 +104,35 @@ m_windowType = NET::OnScreenDisplay; } - connect(surface(), &SurfaceInterface::unmapped, this, &XdgShellClient::unmap); - connect(surface(), &SurfaceInterface::unbound, this, &XdgShellClient::destroyClient); - connect(surface(), &SurfaceInterface::destroyed, this, &XdgShellClient::destroyClient); + connect(surface(), &SurfaceInterface::unmapped, this, &WaylandClient::unmap); + connect(surface(), &SurfaceInterface::unbound, this, &WaylandClient::destroyClient); + connect(surface(), &QObject::destroyed, this, &WaylandClient::destroyClient); + + initType(); + + // set initial desktop + setDesktop(VirtualDesktopManager::self()->current()); + + // setup shadow integration + updateShadow(); + connect(surface(), &SurfaceInterface::shadowChanged, this, &Toplevel::updateShadow); + connect(waylandServer(), &WaylandServer::foreignTransientChanged, this, [this](KWayland::Server::SurfaceInterface *child) { + if (child == surface()) { + handleTransientForChanged(); + } + }); + handleTransientForChanged(); + + AbstractClient::updateColorScheme(QString()); + + connect(surface(), &SurfaceInterface::committed, this, &WaylandClient::finishInit); +} + +void XdgShellClient::initType() +{ if (m_xdgShellToplevel) { - connect(m_xdgShellToplevel, &XdgShellSurfaceInterface::destroyed, this, &XdgShellClient::destroyClient); + connect(m_xdgShellToplevel, &QObject::destroyed, this, &WaylandClient::destroyClient); connect(m_xdgShellToplevel, &XdgShellSurfaceInterface::configureAcknowledged, this, &XdgShellClient::handleConfigureAcknowledged); m_caption = m_xdgShellToplevel->title().simplified(); @@ -157,34 +183,16 @@ } else if (m_xdgShellPopup) { connect(m_xdgShellPopup, &XdgShellPopupInterface::configureAcknowledged, this, &XdgShellClient::handleConfigureAcknowledged); connect(m_xdgShellPopup, &XdgShellPopupInterface::grabRequested, this, &XdgShellClient::handleGrabRequested); - connect(m_xdgShellPopup, &XdgShellPopupInterface::destroyed, this, &XdgShellClient::destroyClient); + connect(m_xdgShellPopup, &QObject::destroyed, this, &XdgShellClient::destroyClient); connect(m_xdgShellPopup, &XdgShellPopupInterface::windowGeometryChanged, this, &XdgShellClient::handleWindowGeometryChanged); } - - // set initial desktop - setDesktop(VirtualDesktopManager::self()->current()); - - // setup shadow integration - updateShadow(); - connect(surface(), &SurfaceInterface::shadowChanged, this, &Toplevel::updateShadow); - - connect(waylandServer(), &WaylandServer::foreignTransientChanged, this, [this](KWayland::Server::SurfaceInterface *child) { - if (child == surface()) { - handleTransientForChanged(); - } - }); - handleTransientForChanged(); - - AbstractClient::updateColorScheme(QString()); - - connect(surface(), &SurfaceInterface::committed, this, &XdgShellClient::finishInit); } -void XdgShellClient::finishInit() +void WaylandClient::finishInit() { - disconnect(surface(), &SurfaceInterface::committed, this, &XdgShellClient::finishInit); + disconnect(surface(), &SurfaceInterface::committed, this, &WaylandClient::finishInit); - connect(surface(), &SurfaceInterface::committed, this, &XdgShellClient::handleCommitted); + connect(surface(), &SurfaceInterface::committed, this, &WaylandClient::handleCommitted); bool needsPlacement = !isInitialPositionSet(); @@ -242,9 +250,10 @@ } m_isInitialized = true; + handleCommitted(); } -void XdgShellClient::destroyClient() +void WaylandClient::destroyClient() { m_closing = true; #ifdef KWIN_BUILD_TABBOX @@ -284,42 +293,42 @@ deleted->unrefWindow(); - m_xdgShellToplevel = nullptr; - m_xdgShellPopup = nullptr; - deleteClient(this); + delete this; } -void XdgShellClient::deleteClient(XdgShellClient *c) +void XdgShellClient::destroyClient() { - delete c; + m_xdgShellToplevel = nullptr; + m_xdgShellPopup = nullptr; + WaylandClient::destroyClient(); } -QRect XdgShellClient::inputGeometry() const +QRect WaylandClient::inputGeometry() const { if (isDecorated()) { return AbstractClient::inputGeometry(); } // TODO: What about sub-surfaces sticking outside the main surface? return m_bufferGeometry; } -QRect XdgShellClient::bufferGeometry() const +QRect WaylandClient::bufferGeometry() const { return m_bufferGeometry; } -QStringList XdgShellClient::activities() const +QStringList WaylandClient::activities() const { // TODO: implement return QStringList(); } -QPoint XdgShellClient::clientContentPos() const +QPoint WaylandClient::clientContentPos() const { return -1 * clientPos(); } -QSize XdgShellClient::clientSize() const +QSize WaylandClient::clientSize() const { const QRect boundingRect = surface()->boundingRect(); return m_windowGeometry.size().boundedTo(boundingRect.size()); @@ -341,14 +350,14 @@ return QSize(INT_MAX, INT_MAX); } -void XdgShellClient::debug(QDebug &stream) const +void WaylandClient::debug(QDebug &stream) const { stream.nospace(); - stream << "\'XdgShellClient:" << surface() << ";WMCLASS:" << resourceClass() << ":" - << resourceName() << ";Caption:" << caption() << "\'"; + stream << objectName() << "(" << surface() << ";WMCLASS:" << resourceClass() << ":" + << resourceName() << ";Caption:" << caption() << ")"; } -bool XdgShellClient::belongsToDesktop() const +bool WaylandClient::belongsToDesktop() const { const auto clients = waylandServer()->clients(); @@ -381,26 +390,26 @@ return AbstractClient::layerForDock(); } -QRect XdgShellClient::transparentRect() const +QRect WaylandClient::transparentRect() const { // TODO: implement return QRect(); } -NET::WindowType XdgShellClient::windowType(bool direct, int supported_types) const +NET::WindowType WaylandClient::windowType(bool direct, int supported_types) const { // TODO: implement Q_UNUSED(direct) Q_UNUSED(supported_types) return m_windowType; } -double XdgShellClient::opacity() const +double WaylandClient::opacity() const { return m_opacity; } -void XdgShellClient::setOpacity(double opacity) +void WaylandClient::setOpacity(double opacity) { const qreal newOpacity = qBound(0.0, opacity, 1.0); if (newOpacity == m_opacity) { @@ -412,16 +421,16 @@ emit opacityChanged(this, oldOpacity); } -void XdgShellClient::addDamage(const QRegion &damage) +void WaylandClient::addDamage(const QRegion &damage) { const int offsetX = m_bufferGeometry.x() - frameGeometry().x(); const int offsetY = m_bufferGeometry.y() - frameGeometry().y(); repaints_region += damage.translated(offsetX, offsetY); Toplevel::addDamage(damage); } -void XdgShellClient::markAsMapped() +void WaylandClient::markAsMapped() { if (!m_unmapped) { return; @@ -437,10 +446,9 @@ if (shouldExposeToWindowManagement()) { setupWindowManagementInterface(); } - updateShowOnScreenEdge(); } -void XdgShellClient::updateDecoration(bool check_workspace_pos, bool force) +void WaylandClient::updateDecoration(bool check_workspace_pos, bool force) { if (!force && ((!isDecorated() && noBorder()) || (isDecorated() && !noBorder()))) @@ -454,6 +462,15 @@ createDecoration(oldgeom); } else destroyDecoration(); + refreshDecoration(); + updateShadow(); + if (check_workspace_pos) + checkWorkspacePosition(oldgeom, -2, oldClientGeom); + blockGeometryUpdates(false); +} + +void XdgShellClient::refreshDecoration() +{ if (m_serverDecoration && isDecorated()) { m_serverDecoration->setMode(KWayland::Server::ServerSideDecorationManagerInterface::Mode::Server); } @@ -464,13 +481,9 @@ m_xdgShellToplevel->configure(xdgSurfaceStates(), m_requestedClientSize); } } - updateShadow(); - if (check_workspace_pos) - checkWorkspacePosition(oldgeom, -2, oldClientGeom); - blockGeometryUpdates(false); } -void XdgShellClient::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t force) +void WaylandClient::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t force) { const QRect newGeometry = rules()->checkGeometry(QRect(x, y, w, h)); @@ -506,7 +519,7 @@ } } -QRect XdgShellClient::determineBufferGeometry() const +QRect WaylandClient::determineBufferGeometry() const { // Offset of the main surface relative to the frame rect. const int offsetX = borderLeft() - m_windowGeometry.left(); @@ -520,7 +533,7 @@ return bufferGeometry; } -void XdgShellClient::doSetGeometry(const QRect &rect) +void WaylandClient::doSetGeometry(const QRect &rect) { bool frameGeometryIsChanged = false; bool bufferGeometryIsChanged = false; @@ -563,19 +576,19 @@ } } -void XdgShellClient::doMove(int x, int y) +void WaylandClient::doMove(int x, int y) { Q_UNUSED(x) Q_UNUSED(y) m_bufferGeometry = determineBufferGeometry(); } -QByteArray XdgShellClient::windowRole() const +QByteArray WaylandClient::windowRole() const { return QByteArray(); } -bool XdgShellClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const +bool WaylandClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const { if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) { if (other->desktopFileName() == desktopFileName()) { @@ -588,22 +601,22 @@ return false; } -void XdgShellClient::blockActivityUpdates(bool b) +void WaylandClient::blockActivityUpdates(bool b) { Q_UNUSED(b) } -QString XdgShellClient::captionNormal() const +QString WaylandClient::captionNormal() const { return m_caption; } -QString XdgShellClient::captionSuffix() const +QString WaylandClient::captionSuffix() const { return m_captionSuffix; } -void XdgShellClient::updateCaption() +void WaylandClient::updateCaption() { const QString oldSuffix = m_captionSuffix; const auto shortcut = shortcutCaptionSuffix(); @@ -628,7 +641,7 @@ } } -AbstractClient *XdgShellClient::findModal(bool allow_itself) +AbstractClient *WaylandClient::findModal(bool allow_itself) { Q_UNUSED(allow_itself) return nullptr; @@ -645,12 +658,12 @@ return false; } -bool XdgShellClient::isFullScreen() const +bool WaylandClient::isFullScreen() const { return m_fullScreen; } -bool XdgShellClient::isMaximizable() const +bool WaylandClient::isMaximizable() const { if (!isResizable()) { return false; @@ -717,18 +730,18 @@ return true; } -bool XdgShellClient::isShown(bool shaded_is_shown) const +bool WaylandClient::isShown(bool shaded_is_shown) const { Q_UNUSED(shaded_is_shown) return !m_closing && !m_unmapped && !isMinimized() && !m_hidden; } -bool XdgShellClient::isHiddenInternal() const +bool WaylandClient::isHiddenInternal() const { return m_unmapped || m_hidden; } -void XdgShellClient::hideClient(bool hide) +void WaylandClient::hideClient(bool hide) { if (m_hidden == hide) { return; @@ -744,7 +757,7 @@ } static bool changeMaximizeRecursion = false; -void XdgShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) +void WaylandClient::changeMaximize(bool horizontal, bool vertical, bool adjust) { if (changeMaximizeRecursion) { return; @@ -780,7 +793,7 @@ // call into decoration update borders if (isDecorated() && decoration()->client() && !(options->borderlessMaximizedWindows() && m_requestedMaximizeMode == KWin::MaximizeFull)) { changeMaximizeRecursion = true; - const auto c = decoration()->client().data(); + const auto c = decoration()->client().toStrongRef(); if ((m_requestedMaximizeMode & MaximizeVertical) != (oldMode & MaximizeVertical)) { emit c->maximizedVerticallyChanged(m_requestedMaximizeMode & MaximizeVertical); } @@ -845,12 +858,12 @@ } } -MaximizeMode XdgShellClient::maximizeMode() const +MaximizeMode WaylandClient::maximizeMode() const { return m_maximizeMode; } -MaximizeMode XdgShellClient::requestedMaximizeMode() const +MaximizeMode WaylandClient::requestedMaximizeMode() const { return m_requestedMaximizeMode; } @@ -868,15 +881,12 @@ return true; } -bool XdgShellClient::isFullScreenable() const +bool WaylandClient::isFullScreenable() const { - if (!rules()->checkFullScreen(true)) { - return false; - } - return !isSpecialWindow(); + return rules()->checkFullScreen(true) || !isSpecialWindow(); } -void XdgShellClient::setFullScreen(bool set, bool user) +void WaylandClient::setFullScreen(bool set, bool user) { set = rules()->checkFullScreen(set); @@ -927,7 +937,7 @@ emit fullScreenChanged(); } -void XdgShellClient::setNoBorder(bool set) +void WaylandClient::setNoBorder(bool set) { if (!userCanSetNoBorder()) { return; @@ -941,26 +951,31 @@ updateWindowRules(Rules::NoBorder); } -void XdgShellClient::setOnAllActivities(bool set) +void WaylandClient::setOnAllActivities(bool set) { Q_UNUSED(set) } -void XdgShellClient::takeFocus() +void XdgShellClient::aboutToActivate() +{ + if (m_xdgShellToplevel) { + ping(PingReason::FocusWindow); + } +} + +void WaylandClient::takeFocus() { if (rules()->checkAcceptFocus(wantsInput())) { - if (m_xdgShellToplevel) { - ping(PingReason::FocusWindow); - } + aboutToActivate(); setActive(true); } if (!keepAbove() && !isOnScreenDisplay() && !belongsToDesktop()) { workspace()->setShowingDesktop(false); } } -void XdgShellClient::doSetActive() +void WaylandClient::doSetActive() { if (!isActive()) { return; @@ -988,7 +1003,7 @@ return false; } -bool XdgShellClient::wantsInput() const +bool WaylandClient::wantsInput() const { return rules()->checkAcceptFocus(acceptsFocus()); } @@ -1024,27 +1039,27 @@ return false; } -void XdgShellClient::createWindowId() +void WaylandClient::createWindowId() { m_windowId = waylandServer()->createWindowId(surface()); } -pid_t XdgShellClient::pid() const +pid_t WaylandClient::pid() const { return surface()->client()->processId(); } -bool XdgShellClient::isLockScreen() const +bool WaylandClient::isLockScreen() const { return surface()->client() == waylandServer()->screenLockerClientConnection(); } -bool XdgShellClient::isInputMethod() const +bool WaylandClient::isInputMethod() const { return surface()->client() == waylandServer()->inputMethodConnection(); } -void XdgShellClient::requestGeometry(const QRect &rect) +void WaylandClient::requestGeometry(const QRect &rect) { if (m_requestGeometryBlockCounter != 0) { m_blockedRequestGeometry = rect; @@ -1059,32 +1074,37 @@ } m_requestedClientSize = size; - quint64 serialId = 0; + const quint64 serialId = configureResize(size, rect); + + if (rect.isValid()) { //if there's no requested size, then there's implicity no positional information worth using + PendingConfigureRequest configureRequest; + configureRequest.serialId = serialId; + configureRequest.positionAfterResize = rect.topLeft(); + configureRequest.maximizeMode = m_requestedMaximizeMode; + m_pendingConfigureRequests.append(configureRequest); + } + + m_blockedRequestGeometry = QRect(); +} +int XdgShellClient::configureResize(const QSize &size, const QRect &newGeometry) +{ + int serialId = 0; if (m_xdgShellToplevel) { serialId = m_xdgShellToplevel->configure(xdgSurfaceStates(), size); } if (m_xdgShellPopup) { auto parent = transientFor(); if (parent) { const QPoint globalClientContentPos = parent->frameGeometry().topLeft() + parent->clientPos(); - const QPoint relativeOffset = rect.topLeft() - globalClientContentPos; + const QPoint relativeOffset = newGeometry.topLeft() - globalClientContentPos; serialId = m_xdgShellPopup->configure(QRect(relativeOffset, size)); } } - - if (rect.isValid()) { //if there's no requested size, then there's implicity no positional information worth using - PendingConfigureRequest configureRequest; - configureRequest.serialId = serialId; - configureRequest.positionAfterResize = rect.topLeft(); - configureRequest.maximizeMode = m_requestedMaximizeMode; - m_pendingConfigureRequests.append(configureRequest); - } - - m_blockedRequestGeometry = QRect(); + return serialId; } -void XdgShellClient::updatePendingGeometry() +void WaylandClient::updatePendingGeometry() { QPoint position = pos(); MaximizeMode maximizeMode = m_maximizeMode; @@ -1116,12 +1136,12 @@ updateMaximizeMode(maximizeMode); } -void XdgShellClient::handleConfigureAcknowledged(quint32 serial) +void WaylandClient::handleConfigureAcknowledged(quint32 serial) { m_lastAckedConfigureRequest = serial; } -void XdgShellClient::handleTransientForChanged() +SurfaceInterface *XdgShellClient::findTransientSurface() { SurfaceInterface *transientSurface = nullptr; if (m_xdgShellToplevel) { @@ -1132,6 +1152,12 @@ if (m_xdgShellPopup) { transientSurface = m_xdgShellPopup->transientFor().data(); } + return transientSurface; +} + +void WaylandClient::handleTransientForChanged() +{ + SurfaceInterface *transientSurface = findTransientSurface(); if (!transientSurface) { transientSurface = waylandServer()->findForeignTransientForSurface(surface()); } @@ -1149,7 +1175,7 @@ m_transient = (transientSurface != nullptr); } -void XdgShellClient::handleWindowClassChanged(const QByteArray &windowClass) +void WaylandClient::handleWindowClassChanged(const QByteArray &windowClass) { setResourceClass(resourceName(), windowClass); if (m_isInitialized && supportsWindowRules()) { @@ -1159,13 +1185,13 @@ setDesktopFileName(windowClass); } -void XdgShellClient::handleWindowGeometryChanged(const QRect &windowGeometry) +void WaylandClient::handleWindowGeometryChanged(const QRect &windowGeometry) { m_windowGeometry = windowGeometry; m_hasWindowGeometry = true; } -void XdgShellClient::handleWindowTitleChanged(const QString &title) +void WaylandClient::handleWindowTitleChanged(const QString &title) { const QString oldSuffix = m_captionSuffix; m_caption = title.simplified(); @@ -1176,15 +1202,15 @@ } } -void XdgShellClient::handleMoveRequested(SeatInterface *seat, quint32 serial) +void WaylandClient::handleMoveRequested(SeatInterface *seat, quint32 serial) { // FIXME: Check the seat and serial. Q_UNUSED(seat) Q_UNUSED(serial) performMouseCommand(Options::MouseMove, Cursor::pos()); } -void XdgShellClient::handleResizeRequested(SeatInterface *seat, quint32 serial, Qt::Edges edges) +void WaylandClient::handleResizeRequested(SeatInterface *seat, quint32 serial, Qt::Edges edges) { // FIXME: Check the seat and serial. Q_UNUSED(seat) @@ -1220,12 +1246,12 @@ updateCursor(); } -void XdgShellClient::handleMinimizeRequested() +void WaylandClient::handleMinimizeRequested() { performMouseCommand(Options::MouseMinimize, Cursor::pos()); } -void XdgShellClient::handleMaximizeRequested(bool maximized) +void WaylandClient::handleMaximizeRequested(bool maximized) { // If the maximized state of the client hasn't been changed due to a window // rule or because the requested state is the same as the current, then the @@ -1235,22 +1261,22 @@ maximize(maximized ? MaximizeFull : MaximizeRestore); } -void XdgShellClient::handleFullScreenRequested(bool fullScreen, OutputInterface *output) +void WaylandClient::handleFullScreenRequested(bool fullScreen, OutputInterface *output) { // FIXME: Consider output as well. Q_UNUSED(output); setFullScreen(fullScreen, false); } -void XdgShellClient::handleWindowMenuRequested(SeatInterface *seat, quint32 serial, const QPoint &surfacePos) +void WaylandClient::handleWindowMenuRequested(SeatInterface *seat, quint32 serial, const QPoint &surfacePos) { // FIXME: Check the seat and serial. Q_UNUSED(seat) Q_UNUSED(serial) performMouseCommand(Options::MouseOperationsMenu, pos() + surfacePos); } -void XdgShellClient::handleGrabRequested(SeatInterface *seat, quint32 serial) +void WaylandClient::handleGrabRequested(SeatInterface *seat, quint32 serial) { // FIXME: Check the seat and serial as well whether the parent had focus. Q_UNUSED(seat) @@ -1294,7 +1320,7 @@ } } -void XdgShellClient::handleCommitted() +void WaylandClient::handleCommitted() { if (!surface()->buffer()) { return; @@ -1310,7 +1336,7 @@ markAsMapped(); } -void XdgShellClient::resizeWithChecks(int w, int h, ForceGeometry_t force) +void WaylandClient::resizeWithChecks(int w, int h, ForceGeometry_t force) { const QRect area = workspace()->clientArea(WorkArea, this); // don't allow growing larger than workarea @@ -1323,7 +1349,7 @@ setFrameGeometry(x(), y(), w, h, force); } -void XdgShellClient::unmap() +void WaylandClient::unmap() { m_unmapped = true; if (isMoveResize()) { @@ -1385,6 +1411,7 @@ workspace()->activateClient(this); } }); + connect(this, &WaylandClient::windowShown, this, &XdgShellClient::updateShowOnScreenEdge); connect(surface, &PlasmaShellSurfaceInterface::positionChanged, this, updatePosition); connect(surface, &PlasmaShellSurfaceInterface::roleChanged, this, updateRole); connect(surface, &PlasmaShellSurfaceInterface::panelBehaviorChanged, this, @@ -1535,6 +1562,11 @@ updatePalette(palette->palette()); } +void WaylandClient::updateColorScheme() +{ + AbstractClient::updateColorScheme(rules()->checkDecoColor(QString())); +} + void XdgShellClient::updateColorScheme() { if (m_paletteInterface) { @@ -1544,7 +1576,7 @@ } } -void XdgShellClient::updateMaximizeMode(MaximizeMode maximizeMode) +void WaylandClient::updateMaximizeMode(MaximizeMode maximizeMode) { if (maximizeMode == m_maximizeMode) { return; @@ -1571,30 +1603,29 @@ return m_plasmaShellSurface->panelBehavior() == PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible; } -quint32 XdgShellClient::windowId() const +quint32 WaylandClient::windowId() const { return m_windowId; } -void XdgShellClient::updateIcon() +void WaylandClient::updateIcon() { - const QString waylandIconName = QStringLiteral("wayland"); const QString dfIconName = iconFromDesktopFile(); - const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName; + const QString iconName = dfIconName.isEmpty() ? QStringLiteral("wayland") : dfIconName; if (iconName == icon().name()) { return; } setIcon(QIcon::fromTheme(iconName)); } -bool XdgShellClient::isTransient() const +bool WaylandClient::isTransient() const { return m_transient; } bool XdgShellClient::hasTransientPlacementHint() const { - return isTransient() && transientFor() && m_xdgShellPopup; + return m_xdgShellPopup && isTransient() && transientFor(); } QRect XdgShellClient::transientPlacement(const QRect &bounds) const @@ -1784,12 +1815,12 @@ return anchorPoint + popupPosAdjust; } -void XdgShellClient::doResizeSync() +void WaylandClient::doResizeSync() { requestGeometry(moveResizeGeometry()); } -QMatrix4x4 XdgShellClient::inputTransformation() const +QMatrix4x4 WaylandClient::inputTransformation() const { QMatrix4x4 matrix; matrix.translate(-m_bufferGeometry.x(), -m_bufferGeometry.y()); @@ -1802,7 +1833,7 @@ return; } m_serverDecoration = deco; - connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed, this, + connect(m_serverDecoration, &QObject::destroyed, this, [this] { m_serverDecoration = nullptr; if (m_closing || !Workspace::self()) { @@ -1850,18 +1881,17 @@ }); } +bool WaylandClient::shouldExposeToWindowManagement() +{ + return !isLockScreen(); +} + bool XdgShellClient::shouldExposeToWindowManagement() { - if (isLockScreen()) { - return false; - } - if (m_xdgShellPopup) { - return false; - } - return true; + return !m_xdgShellPopup || WaylandClient::shouldExposeToWindowManagement(); } -KWayland::Server::XdgShellSurfaceInterface::States XdgShellClient::xdgSurfaceStates() const +KWayland::Server::XdgShellSurfaceInterface::States WaylandClient::xdgSurfaceStates() const { XdgShellSurfaceInterface::States states; if (isActive()) { @@ -1879,7 +1909,7 @@ return states; } -void XdgShellClient::doMinimize() +void WaylandClient::doMinimize() { if (isMinimized()) { workspace()->clientHidden(this); @@ -1911,7 +1941,7 @@ return false; } -void XdgShellClient::killWindow() +void WaylandClient::killWindow() { if (!surface()) { return; @@ -1923,15 +1953,15 @@ } ::kill(c->processId(), SIGTERM); // give it time to terminate and only if terminate fails, try destroy Wayland connection - QTimer::singleShot(5000, c, &ClientConnection::destroy); + QTimer::singleShot(5000, c, &KWayland::Server::ClientConnection::destroy); } -bool XdgShellClient::isLocalhost() const +bool WaylandClient::isLocalhost() const { return true; } -bool XdgShellClient::hasPopupGrab() const +bool WaylandClient::hasPopupGrab() const { return m_hasPopupGrab; } @@ -1943,7 +1973,7 @@ } } -void XdgShellClient::updateClientOutputs() +void WaylandClient::updateClientOutputs() { QVector clientOutputs; const auto outputs = waylandServer()->display()->outputs(); @@ -1975,14 +2005,14 @@ return m_xdgShellToplevel; } -QRect XdgShellClient::adjustMoveGeometry(const QRect &rect) const +QRect WaylandClient::adjustMoveGeometry(const QRect &rect) const { QRect geometry = rect; geometry.moveTopLeft(moveResizeGeometry().topLeft()); return geometry; } -QRect XdgShellClient::adjustResizeGeometry(const QRect &rect) const +QRect WaylandClient::adjustResizeGeometry(const QRect &rect) const { QRect geometry = rect;