diff --git a/shell_client.cpp b/shell_client.cpp index c0c543352..118f7ef6e 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -1,1010 +1,1125 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "shell_client.h" #include "composite.h" #include "cursor.h" #include "deleted.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include "virtualdesktops.h" #include "workspace.h" #include "decorations/decorationbridge.h" #include "decorations/decoratedclient.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include using namespace KWayland::Server; namespace KWin { ShellClient::ShellClient(ShellSurfaceInterface *surface) : AbstractClient() , m_shellSurface(surface) + , m_xdgShellSurface(nullptr) + , m_xdgShellPopup(nullptr) + , m_internal(surface->client() == waylandServer()->internalConnection()) +{ + setSurface(surface->surface()); + init(); +} + +ShellClient::ShellClient(XdgSurfaceV5Interface *surface) + : AbstractClient() + , m_shellSurface(nullptr) + , m_xdgShellSurface(surface) + , m_xdgShellPopup(nullptr) + , m_internal(surface->client() == waylandServer()->internalConnection()) +{ + setSurface(surface->surface()); + init(); +} + +ShellClient::ShellClient(XdgPopupV5Interface *surface) + : AbstractClient() + , m_shellSurface(nullptr) + , m_xdgShellSurface(nullptr) + , m_xdgShellPopup(surface) , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); init(); } ShellClient::~ShellClient() = default; +template +void ShellClient::initSurface(T *shellSurface) +{ + connect(shellSurface, &T::titleChanged, this, &ShellClient::captionChanged); + connect(shellSurface, &T::destroyed, this, &ShellClient::destroyClient); + connect(shellSurface, &T::moveRequested, this, + [this] { + // TODO: check the seat and serial + performMouseCommand(Options::MouseMove, Cursor::pos()); + } + ); + connect(shellSurface, &T::windowClassChanged, this, &ShellClient::updateIcon); + + setResourceClass(shellSurface->windowClass()); + connect(shellSurface, &T::windowClassChanged, this, + [this] (const QByteArray &windowClass) { + setResourceClass(windowClass); + } + ); + connect(shellSurface, &T::resizeRequested, this, + [this] (SeatInterface *seat, quint32 serial, Qt::Edges edges) { + // TODO: check the seat and serial + Q_UNUSED(seat) + Q_UNUSED(serial) + if (!isResizable() || isShade()) { + return; + } + if (isMoveResize()) { + finishMoveResize(false); + } + setMoveResizePointerButtonDown(true); + setMoveOffset(Cursor::pos() - pos()); // map from global + setInvertedMoveOffset(rect().bottomRight() - moveOffset()); + setUnrestrictedMoveResize(false); + auto toPosition = [edges] { + Position pos = PositionCenter; + if (edges.testFlag(Qt::TopEdge)) { + pos = PositionTop; + } else if (edges.testFlag(Qt::BottomEdge)) { + pos = PositionBottom; + } + if (edges.testFlag(Qt::LeftEdge)) { + pos = Position(pos | PositionLeft); + } else if (edges.testFlag(Qt::RightEdge)) { + pos = Position(pos | PositionRight); + } + return pos; + }; + setMoveResizePointerMode(toPosition()); + if (!startMoveResize()) + setMoveResizePointerButtonDown(false); + updateCursor(); + } + ); + connect(shellSurface, &T::maximizedChanged, this, + [this] (bool maximized) { + maximize(maximized ? MaximizeFull : MaximizeRestore); + } + ); + // TODO: consider output! + connect(shellSurface, &T::fullscreenChanged, this, &ShellClient::clientFullScreenChanged); + + connect(shellSurface, &T::transientForChanged, this, &ShellClient::setTransient); +} + void ShellClient::init() { findInternalWindow(); createWindowId(); setupCompositing(); SurfaceInterface *s = surface(); Q_ASSERT(s); if (s->buffer()) { setReadyForPainting(); setupWindowManagementInterface(); m_unmapped = false; m_clientSize = s->buffer()->size(); } else { ready_for_painting = false; } if (m_internalWindow) { updateInternalWindowGeometry(); setOnAllDesktops(true); } else { doSetGeometry(QRect(QPoint(0, 0), m_clientSize)); setDesktop(VirtualDesktopManager::self()->current()); } if (waylandServer()->inputMethodConnection() == s->client()) { m_windowType = NET::OnScreenDisplay; } connect(s, &SurfaceInterface::sizeChanged, this, [this] { m_clientSize = surface()->buffer()->size(); doSetGeometry(QRect(geom.topLeft(), m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); discardWindowPixmap(); } ); connect(s, &SurfaceInterface::unmapped, this, &ShellClient::unmap); if (m_shellSurface) { - connect(m_shellSurface, &ShellSurfaceInterface::destroyed, this, &ShellClient::destroyClient); - connect(m_shellSurface, &ShellSurfaceInterface::titleChanged, this, &ShellClient::captionChanged); - - connect(m_shellSurface, &ShellSurfaceInterface::fullscreenChanged, this, &ShellClient::clientFullScreenChanged); - connect(m_shellSurface, &ShellSurfaceInterface::maximizedChanged, this, - [this] (bool maximized) { - maximize(maximized ? MaximizeFull : MaximizeRestore); - } - ); - connect(m_shellSurface, &ShellSurfaceInterface::windowClassChanged, this, &ShellClient::updateIcon); - - setResourceClass(m_shellSurface->windowClass()); - connect(m_shellSurface, &ShellSurfaceInterface::windowClassChanged, this, - [this] { - setResourceClass(m_shellSurface->windowClass()); + initSurface(m_shellSurface); + } else if (m_xdgShellSurface) { + initSurface(m_xdgShellSurface); + connect(m_xdgShellSurface, &XdgSurfaceV5Interface::windowMenuRequested, this, + [this] (SeatInterface *seat, quint32 serial, const QPoint &surfacePos) { + // TODO: check serial on seat + Q_UNUSED(seat) + Q_UNUSED(serial) + performMouseCommand(Options::MouseOperationsMenu, pos() + surfacePos); } ); - connect(m_shellSurface, &ShellSurfaceInterface::transientForChanged, this, &ShellClient::setTransient); - connect(m_shellSurface, &ShellSurfaceInterface::moveRequested, this, + connect(m_xdgShellSurface, &XdgSurfaceV5Interface::minimizeRequested, this, [this] { - // TODO: check the seat and serial - performMouseCommand(Options::MouseMove, Cursor::pos()); - } - ); - connect(m_shellSurface, &ShellSurfaceInterface::resizeRequested, this, - [this] (SeatInterface *seat, quint32 serial, Qt::Edges edges) { - // TODO: check the seat and serial - Q_UNUSED(seat) - Q_UNUSED(serial) - if (!isResizable() || isShade()) { - return; - } - if (isMoveResize()) { - finishMoveResize(false); - } - setMoveResizePointerButtonDown(true); - setMoveOffset(Cursor::pos() - pos()); // map from global - setInvertedMoveOffset(rect().bottomRight() - moveOffset()); - setUnrestrictedMoveResize(false); - auto toPosition = [edges] { - Position pos = PositionCenter; - if (edges.testFlag(Qt::TopEdge)) { - pos = PositionTop; - } else if (edges.testFlag(Qt::BottomEdge)) { - pos = PositionBottom; - } - if (edges.testFlag(Qt::LeftEdge)) { - pos = Position(pos | PositionLeft); - } else if (edges.testFlag(Qt::RightEdge)) { - pos = Position(pos | PositionRight); - } - return pos; - }; - setMoveResizePointerMode(toPosition()); - if (!startMoveResize()) - setMoveResizePointerButtonDown(false); - updateCursor(); + performMouseCommand(Options::MouseMinimize, Cursor::pos()); } ); + auto configure = [this] { + m_xdgShellSurface->configure(xdgSurfaceStates()); + }; + connect(this, &AbstractClient::activeChanged, this, configure); + connect(this, &AbstractClient::clientStartUserMovedResized, this, configure); + connect(this, &AbstractClient::clientFinishUserMovedResized, this, configure); + } else if (m_xdgShellPopup) { + connect(m_xdgShellPopup, &XdgPopupV5Interface::destroyed, this, &ShellClient::destroyClient); } updateIcon(); // setup shadow integration getShadow(); connect(s, &SurfaceInterface::shadowChanged, this, &Toplevel::getShadow); setTransient(); // check whether we have a ServerSideDecoration if (ServerSideDecorationInterface *deco = ServerSideDecorationInterface::get(s)) { installServerSideDecoration(deco); } updateColorScheme(QString()); } void ShellClient::destroyClient() { m_closing = true; Deleted *del = nullptr; if (workspace()) { del = Deleted::create(this); } emit windowClosed(this, del); destroyDecoration(); if (workspace()) { StackingUpdatesBlocker blocker(workspace()); if (transientFor()) { transientFor()->removeTransient(this); } for (auto it = transients().constBegin(); it != transients().constEnd();) { if ((*it)->transientFor() == this) { removeTransient(*it); it = transients().constBegin(); // restart, just in case something more has changed with the list } else { ++it; } } } waylandServer()->removeClient(this); if (del) { del->unrefWindow(); } m_shellSurface = nullptr; deleteClient(this); } void ShellClient::deleteClient(ShellClient *c) { delete c; } QStringList ShellClient::activities() const { // TODO: implement return QStringList(); } QPoint ShellClient::clientContentPos() const { return -1 * clientPos(); } QSize ShellClient::clientSize() const { // TODO: connect for changes return m_clientSize; } void ShellClient::debug(QDebug &stream) const { // TODO: implement Q_UNUSED(stream) } Layer ShellClient::layerForDock() const { if (m_plasmaShellSurface) { switch (m_plasmaShellSurface->panelBehavior()) { case PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover: return NormalLayer; case PlasmaShellSurfaceInterface::PanelBehavior::AutoHide: return AboveLayer; case PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow: case PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible: return DockLayer; default: Q_UNREACHABLE(); break; } } return AbstractClient::layerForDock(); } bool ShellClient::shouldUnredirect() const { // TODO: unredirect for fullscreen return false; } QRect ShellClient::transparentRect() const { // TODO: implement return QRect(); } NET::WindowType ShellClient::windowType(bool direct, int supported_types) const { // TODO: implement Q_UNUSED(direct) Q_UNUSED(supported_types) return m_windowType; } double ShellClient::opacity() const { return m_opacity; } void ShellClient::setOpacity(double opacity) { const qreal newOpacity = qBound(0.0, opacity, 1.0); if (newOpacity == m_opacity) { return; } const qreal oldOpacity = m_opacity; m_opacity = newOpacity; addRepaintFull(); emit opacityChanged(this, oldOpacity); } void ShellClient::addDamage(const QRegion &damage) { auto s = surface(); if (s->buffer()->size().isValid()) { m_clientSize = s->buffer()->size(); QPoint position = geom.topLeft(); if (m_positionAfterResize.isValid()) { addLayerRepaint(geometry()); position = m_positionAfterResize.point(); m_positionAfterResize.clear(); } doSetGeometry(QRect(position, m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); } markAsMapped(); setDepth(s->buffer()->hasAlphaChannel() ? 32 : 24); repaints_region += damage.translated(clientPos()); Toplevel::addDamage(damage); } void ShellClient::setInternalFramebufferObject(const QSharedPointer &fbo) { if (fbo.isNull()) { unmap(); return; } markAsMapped(); m_clientSize = fbo->size(); doSetGeometry(QRect(geom.topLeft(), m_clientSize)); Toplevel::setInternalFramebufferObject(fbo); Toplevel::addDamage(QRegion(0, 0, width(), height())); } void ShellClient::markAsMapped() { if (!m_unmapped) { return; } m_unmapped = false; setReadyForPainting(); setupWindowManagementInterface(); } void ShellClient::createDecoration(const QRect &oldGeom) { KDecoration2::Decoration *decoration = Decoration::DecorationBridge::self()->createDecoration(this); if (decoration) { QMetaObject::invokeMethod(decoration, "update", Qt::QueuedConnection); connect(decoration, &KDecoration2::Decoration::shadowChanged, this, &Toplevel::getShadow); connect(decoration, &KDecoration2::Decoration::bordersChanged, this, [this]() { GeometryUpdatesBlocker blocker(this); QRect oldgeom = geometry(); if (!isShade()) checkWorkspacePosition(oldgeom); emit geometryShapeChanged(this, oldgeom); } ); } setDecoration(decoration); emit geometryShapeChanged(this, oldGeom); } void ShellClient::updateDecoration(bool check_workspace_pos, bool force) { if (!force && ((!isDecorated() && noBorder()) || (isDecorated() && !noBorder()))) return; QRect oldgeom = geometry(); QRect oldClientGeom = oldgeom.adjusted(borderLeft(), borderTop(), -borderRight(), -borderBottom()); blockGeometryUpdates(true); if (force) destroyDecoration(); if (!noBorder()) { createDecoration(oldgeom); } else destroyDecoration(); if (m_serverDecoration && isDecorated()) { m_serverDecoration->setMode(KWayland::Server::ServerSideDecorationManagerInterface::Mode::Server); } getShadow(); if (check_workspace_pos) checkWorkspacePosition(oldgeom, -2, oldClientGeom); blockGeometryUpdates(false); } void ShellClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force) { Q_UNUSED(force) // TODO: better merge with Client's implementation if (QSize(w, h) == geom.size()) { // size didn't change, update directly doSetGeometry(QRect(x, y, w, h)); } else { // size did change, Client needs to provide a new buffer requestGeometry(QRect(x, y, w, h)); } } void ShellClient::doSetGeometry(const QRect &rect) { if (geom == rect) { return; } if (!m_unmapped) { addWorkspaceRepaint(visibleRect()); } const QRect old = geom; geom = rect; if (m_unmapped && m_geomMaximizeRestore.isEmpty() && !geom.isEmpty()) { // use first valid geometry as restore geometry // TODO: needs to interact with placing. The first valid geometry should be the placed one m_geomMaximizeRestore = geom; } if (!m_unmapped) { addWorkspaceRepaint(visibleRect()); } triggerDecorationRepaint(); emit geometryShapeChanged(this, old); } QByteArray ShellClient::windowRole() const { return QByteArray(); } bool ShellClient::belongsToSameApplication(const AbstractClient *other, bool active_hack) const { Q_UNUSED(active_hack) if (auto s = other->surface()) { return s->client() == surface()->client(); } return false; } void ShellClient::blockActivityUpdates(bool b) { Q_UNUSED(b) } QString ShellClient::caption(bool full, bool stripped) const { Q_UNUSED(full) Q_UNUSED(stripped) if (m_shellSurface) { return m_shellSurface->title(); } + if (m_xdgShellSurface) { + return m_xdgShellSurface->title(); + } return QString(); } void ShellClient::closeWindow() { + if (m_xdgShellSurface && isCloseable()) { + m_xdgShellSurface->close(); + } if (m_qtExtendedSurface && isCloseable()) { m_qtExtendedSurface->close(); } } AbstractClient *ShellClient::findModal(bool allow_itself) { Q_UNUSED(allow_itself) return nullptr; } bool ShellClient::isCloseable() const { if (m_windowType == NET::Desktop || m_windowType == NET::Dock) { return false; } + if (m_xdgShellSurface) { + return true; + } return m_qtExtendedSurface ? true : false; } bool ShellClient::isFullScreenable() const { return false; } bool ShellClient::isFullScreen() const { return m_fullScreen; } bool ShellClient::isMaximizable() const { return true; } bool ShellClient::isMinimizable() const { return (!m_plasmaShellSurface || m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Normal); } QRect ShellClient::iconGeometry() const { if (!windowManagementInterface()) { // window management interface is only available if the surface is mapped return QRect(); } int minDistance = INT_MAX; ShellClient *candidatePanel = nullptr; QRect candidateGeom; for (auto i = windowManagementInterface()->minimizedGeometries().constBegin(), end = windowManagementInterface()->minimizedGeometries().constEnd(); i != end; ++i) { ShellClient *client = waylandServer()->findClient(i.key()); if (!client) { continue; } const int distance = QPoint(client->pos() - pos()).manhattanLength(); if (distance < minDistance) { minDistance = distance; candidatePanel = client; candidateGeom = i.value(); } } if (!candidatePanel) { return QRect(); } return candidateGeom.translated(candidatePanel->pos()); } bool ShellClient::isMovable() const { if (m_plasmaShellSurface) { return m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Normal; } + if (m_xdgShellPopup) { + return false; + } return true; } bool ShellClient::isMovableAcrossScreens() const { if (m_plasmaShellSurface) { return m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Normal; } + if (m_xdgShellPopup) { + return false; + } return true; } bool ShellClient::isResizable() const { if (m_plasmaShellSurface) { return m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Normal; } + if (m_xdgShellPopup) { + return false; + } return true; } bool ShellClient::isShown(bool shaded_is_shown) const { Q_UNUSED(shaded_is_shown) return !m_closing && !m_unmapped; } void ShellClient::hideClient(bool hide) { Q_UNUSED(hide) } void ShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) { StackingUpdatesBlocker blocker(workspace()); // 'adjust == true' means to update the size only, e.g. after changing workspace size if (!adjust) { if (vertical) m_maximizeMode = MaximizeMode(m_maximizeMode ^ MaximizeVertical); if (horizontal) m_maximizeMode = MaximizeMode(m_maximizeMode ^ MaximizeHorizontal); } // TODO: check rules if (m_maximizeMode == MaximizeFull) { m_geomMaximizeRestore = geometry(); requestGeometry(workspace()->clientArea(MaximizeArea, this)); workspace()->raiseClient(this); } else { if (m_geomMaximizeRestore.isValid()) { requestGeometry(m_geomMaximizeRestore); } else { requestGeometry(workspace()->clientArea(PlacementArea, this)); } } // TODO: add more checks as in Client } MaximizeMode ShellClient::maximizeMode() const { return m_maximizeMode; } bool ShellClient::noBorder() const { if (isInternal()) { return true; } if (m_serverDecoration) { if (m_serverDecoration->mode() == ServerSideDecorationManagerInterface::Mode::Server) { return m_userNoBorder; } } return true; } const WindowRules *ShellClient::rules() const { static WindowRules s_rules; return &s_rules; } void ShellClient::setFullScreen(bool set, bool user) { Q_UNUSED(set) Q_UNUSED(user) } void ShellClient::setNoBorder(bool set) { if (!userCanSetNoBorder()) { return; } set = rules()->checkNoBorder(set); if (m_userNoBorder == set) { return; } m_userNoBorder = set; updateDecoration(true, false); updateWindowRules(Rules::NoBorder); } void ShellClient::setOnAllActivities(bool set) { Q_UNUSED(set) } void ShellClient::setShortcut(const QString &cut) { Q_UNUSED(cut) } const QKeySequence &ShellClient::shortcut() const { static QKeySequence seq; return seq; } void ShellClient::takeFocus() { if (rules()->checkAcceptFocus(wantsInput())) { setActive(true); } bool breakShowingDesktop = !keepAbove() && !isOnScreenDisplay(); if (breakShowingDesktop) { // check that it doesn't belong to the desktop const auto &clients = waylandServer()->clients(); for (auto c: clients) { if (!belongsToSameApplication(c, false)) { continue; } if (c->isDesktop()) { breakShowingDesktop = false; break; } } } if (breakShowingDesktop) workspace()->setShowingDesktop(false); } void ShellClient::doSetActive() { StackingUpdatesBlocker blocker(workspace()); workspace()->focusToNull(); } void ShellClient::updateWindowRules(Rules::Types selection) { Q_UNUSED(selection) } bool ShellClient::userCanSetFullScreen() const { return false; } bool ShellClient::userCanSetNoBorder() const { if (m_serverDecoration && m_serverDecoration->mode() == ServerSideDecorationManagerInterface::Mode::Server) { return !isFullScreen() && !isShade() && !tabGroup(); } return false; } bool ShellClient::wantsInput() const { return rules()->checkAcceptFocus(acceptsFocus()); } bool ShellClient::acceptsFocus() const { if (isInternal()) { return false; } if (waylandServer()->inputMethodConnection() == surface()->client()) { return false; } if (m_shellSurface) { if (m_shellSurface->isPopup()) { return false; } // if the window is not visible it doesn't get input return m_shellSurface->acceptsKeyboardFocus() && isShown(true); } + if (m_xdgShellSurface) { + // TODO: proper + return true; + } return false; } void ShellClient::createWindowId() { if (m_internalWindow) { m_windowId = m_internalWindow->winId(); } else { m_windowId = waylandServer()->createWindowId(surface()); } } void ShellClient::findInternalWindow() { if (surface()->client() != waylandServer()->internalConnection()) { return; } const QWindowList windows = kwinApp()->topLevelWindows(); for (QWindow *w: windows) { auto s = KWayland::Client::Surface::fromWindow(w); if (!s) { continue; } if (s->id() != surface()->id()) { continue; } m_internalWindow = w; connect(m_internalWindow, &QWindow::xChanged, this, &ShellClient::updateInternalWindowGeometry); connect(m_internalWindow, &QWindow::yChanged, this, &ShellClient::updateInternalWindowGeometry); connect(m_internalWindow, &QWindow::destroyed, this, [this] { m_internalWindow = nullptr; }); // Try reading the window type from the QWindow. PlasmaCore.Dialog provides a dynamic type property // let's check whether it exists, if it does it's our window type const QVariant windowType = m_internalWindow->property("type"); if (!windowType.isNull()) { m_windowType = static_cast(windowType.toInt()); } return; } } void ShellClient::updateInternalWindowGeometry() { if (!m_internalWindow) { return; } doSetGeometry(m_internalWindow->geometry()); } bool ShellClient::isInternal() const { return m_internal; } bool ShellClient::isLockScreen() const { return surface()->client() == waylandServer()->screenLockerClientConnection(); } bool ShellClient::isInputMethod() const { return surface()->client() == waylandServer()->inputMethodConnection(); } xcb_window_t ShellClient::window() const { return windowId(); } void ShellClient::requestGeometry(const QRect &rect) { m_positionAfterResize.setPoint(rect.topLeft()); + const QSize size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); if (m_shellSurface) { - m_shellSurface->requestSize(rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); + m_shellSurface->requestSize(size); + } + if (m_xdgShellSurface) { + m_xdgShellSurface->configure(xdgSurfaceStates(), size); } } void ShellClient::clientFullScreenChanged(bool fullScreen) { StackingUpdatesBlocker blocker(workspace()); workspace()->updateClientLayer(this); // active fullscreens get different layer + const bool emitSignal = m_fullScreen != fullScreen; + m_fullScreen = fullScreen; + if (fullScreen) { m_geomFsRestore = geometry(); requestGeometry(workspace()->clientArea(FullScreenArea, this)); workspace()->raiseClient(this); } else { if (m_geomFsRestore.isValid()) { requestGeometry(m_geomFsRestore); } else { requestGeometry(workspace()->clientArea(MaximizeArea, this)); } } - if (m_fullScreen != fullScreen) { - m_fullScreen = fullScreen; + if (emitSignal) { emit fullScreenChanged(); } } void ShellClient::resizeWithChecks(int w, int h, ForceGeometry_t force) { Q_UNUSED(force) QRect area = workspace()->clientArea(WorkArea, this); // don't allow growing larger than workarea if (w > area.width()) { w = area.width(); } if (h > area.height()) { h = area.height(); } if (m_shellSurface) { m_shellSurface->requestSize(QSize(w, h)); } + if (m_xdgShellSurface) { + m_xdgShellSurface->configure(xdgSurfaceStates(), QSize(w, h)); + } } void ShellClient::unmap() { m_unmapped = true; ready_for_painting = false; destroyWindowManagementInterface(); if (Workspace::self()) { addWorkspaceRepaint(visibleRect()); workspace()->clientHidden(this); } emit windowHidden(this); } void ShellClient::installPlasmaShellSurface(PlasmaShellSurfaceInterface *surface) { m_plasmaShellSurface = surface; auto updatePosition = [this, surface] { doSetGeometry(QRect(surface->position(), m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); }; auto updateRole = [this, surface] { NET::WindowType type = NET::Unknown; switch (surface->role()) { case PlasmaShellSurfaceInterface::Role::Desktop: type = NET::Desktop; break; case PlasmaShellSurfaceInterface::Role::Panel: type = NET::Dock; break; case PlasmaShellSurfaceInterface::Role::OnScreenDisplay: type = NET::OnScreenDisplay; break; case PlasmaShellSurfaceInterface::Role::Normal: default: type = NET::Normal; break; } if (type != m_windowType) { m_windowType = type; workspace()->updateClientArea(); } }; connect(surface, &PlasmaShellSurfaceInterface::positionChanged, this, updatePosition); connect(surface, &PlasmaShellSurfaceInterface::roleChanged, this, updateRole); connect(surface, &PlasmaShellSurfaceInterface::panelBehaviorChanged, this, [] { workspace()->updateClientArea(); } ); updatePosition(); updateRole(); setSkipTaskbar(surface->skipTaskbar()); connect(surface, &PlasmaShellSurfaceInterface::skipTaskbarChanged, this, [this] { setSkipTaskbar(m_plasmaShellSurface->skipTaskbar()); }); } bool ShellClient::isInitialPositionSet() const { if (m_plasmaShellSurface) { return m_plasmaShellSurface->isPositionSet(); } return false; } void ShellClient::installQtExtendedSurface(QtExtendedSurfaceInterface *surface) { m_qtExtendedSurface = surface; connect(m_qtExtendedSurface.data(), &QtExtendedSurfaceInterface::raiseRequested, this, [this]() { workspace()->raiseClientRequest(this); }); connect(m_qtExtendedSurface.data(), &QtExtendedSurfaceInterface::lowerRequested, this, [this]() { workspace()->lowerClientRequest(this); }); } bool ShellClient::hasStrut() const { if (!isShown(true)) { return false; } if (!m_plasmaShellSurface) { return false; } if (m_plasmaShellSurface->role() != PlasmaShellSurfaceInterface::Role::Panel) { return false; } return m_plasmaShellSurface->panelBehavior() != PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow; } void ShellClient::updateIcon() { QString desktopFile; if (m_shellSurface) { desktopFile = QString::fromUtf8(m_shellSurface->windowClass()); } if (desktopFile.isEmpty()) { setIcon(QIcon()); } if (!desktopFile.endsWith(QLatin1String(".desktop"))) { desktopFile.append(QLatin1String(".desktop")); } KDesktopFile df(desktopFile); setIcon(QIcon::fromTheme(df.readIcon())); } bool ShellClient::isTransient() const { return m_transient; } void ShellClient::setTransient() { SurfaceInterface *s = nullptr; if (m_shellSurface) { s = m_shellSurface->transientFor().data(); } + if (m_xdgShellSurface) { + if (auto transient = m_xdgShellSurface->transientFor().data()) { + s = transient->surface(); + } + } + if (m_xdgShellPopup) { + s = m_xdgShellPopup->transientFor().data(); + } auto t = waylandServer()->findClient(s); if (t != transientFor()) { // remove from main client if (transientFor()) transientFor()->removeTransient(this); setTransientFor(t); if (t) { t->addTransient(this); } } m_transient = (s != nullptr); } bool ShellClient::hasTransientPlacementHint() const { return isTransient() && transientFor() != nullptr; } QPoint ShellClient::transientPlacementHint() const { if (m_shellSurface) { return m_shellSurface->transientOffset(); } + if (m_xdgShellPopup) { + return m_xdgShellPopup->transientOffset(); + } return QPoint(); } bool ShellClient::isWaitingForMoveResizeSync() const { return m_positionAfterResize.isValid(); } void ShellClient::doResizeSync() { requestGeometry(moveResizeGeometry()); } QMatrix4x4 ShellClient::inputTransformation() const { QMatrix4x4 m = Toplevel::inputTransformation(); m.translate(-borderLeft(), -borderTop()); return m; } void ShellClient::installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *deco) { if (m_serverDecoration == deco) { return; } m_serverDecoration = deco; connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed, this, [this] { m_serverDecoration = nullptr; if (!Workspace::self()) { return; } updateDecoration(true); } ); if (!m_unmapped) { updateDecoration(true); } connect(m_serverDecoration, &ServerSideDecorationInterface::modeRequested, this, [this] (ServerSideDecorationManagerInterface::Mode mode) { const bool changed = mode != m_serverDecoration->mode(); // always acknowledge the requested mode m_serverDecoration->setMode(mode); if (changed && !m_unmapped) { updateDecoration(false); } } ); } +KWayland::Server::XdgSurfaceV5Interface::States ShellClient::xdgSurfaceStates() const +{ + XdgSurfaceV5Interface::States states; + if (isActive()) { + states |= XdgSurfaceV5Interface::State::Activated; + } + if (isFullScreen()) { + states |= XdgSurfaceV5Interface::State::Fullscreen; + } + if (maximizeMode() == MaximizeMode::MaximizeFull) { + states |= XdgSurfaceV5Interface::State::Maximized; + } + if (isResize()) { + states |= XdgSurfaceV5Interface::State::Resizing; + } + return states; +} + } diff --git a/shell_client.h b/shell_client.h index 0eadb581d..efe0785e2 100644 --- a/shell_client.h +++ b/shell_client.h @@ -1,181 +1,189 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_SHELL_CLIENT_H #define KWIN_SHELL_CLIENT_H #include "abstract_client.h" +#include namespace KWayland { namespace Server { class ShellSurfaceInterface; class ServerSideDecorationInterface; class PlasmaShellSurfaceInterface; class QtExtendedSurfaceInterface; } } namespace KWin { class KWIN_EXPORT ShellClient : public AbstractClient { Q_OBJECT public: ShellClient(KWayland::Server::ShellSurfaceInterface *surface); + ShellClient(KWayland::Server::XdgSurfaceV5Interface *surface); + ShellClient(KWayland::Server::XdgPopupV5Interface *surface); virtual ~ShellClient(); QStringList activities() const override; QPoint clientContentPos() const override; QSize clientSize() const override; QRect transparentRect() const override; bool shouldUnredirect() const override; NET::WindowType windowType(bool direct = false, int supported_types = 0) const override; void debug(QDebug &stream) const override; double opacity() const override; void setOpacity(double opacity) override; QByteArray windowRole() const override; KWayland::Server::ShellSurfaceInterface *shellSurface() const { return m_shellSurface; } void blockActivityUpdates(bool b = true) override; QString caption(bool full = true, bool stripped = false) 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; QRect iconGeometry() const override; bool isMovable() const override; bool isMovableAcrossScreens() const override; bool isResizable() const override; bool isShown(bool shaded_is_shown) const override; void hideClient(bool hide) override; MaximizeMode maximizeMode() const override; QRect geometryRestore() const override { return m_geomMaximizeRestore; } bool noBorder() const override; const WindowRules *rules() 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 setShortcut(const QString &cut) override; const QKeySequence &shortcut() const override; void takeFocus() override; void updateWindowRules(Rules::Types selection) override; bool userCanSetFullScreen() const override; bool userCanSetNoBorder() const override; bool wantsInput() const override; xcb_window_t window() const override; using AbstractClient::resizeWithChecks; void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet) override; using AbstractClient::setGeometry; void setGeometry(int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet) override; bool hasStrut() const override; void setInternalFramebufferObject(const QSharedPointer &fbo) override; quint32 windowId() const { return m_windowId; } bool isInternal() const; bool isLockScreen() const override; bool isInputMethod() const override; QWindow *internalWindow() const { return m_internalWindow; } void installPlasmaShellSurface(KWayland::Server::PlasmaShellSurfaceInterface *surface); void installQtExtendedSurface(KWayland::Server::QtExtendedSurfaceInterface *surface); void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); bool isInitialPositionSet() const; bool isTransient() const override; bool hasTransientPlacementHint() const override; QPoint transientPlacementHint() const override; QMatrix4x4 inputTransformation() const override; protected: void addDamage(const QRegion &damage) override; bool belongsToSameApplication(const AbstractClient *other, bool active_hack) const override; void doSetActive() override; Layer layerForDock() const override; void changeMaximize(bool horizontal, bool vertical, bool adjust) override; void setGeometryRestore(const QRect &geo) override { m_geomMaximizeRestore = geo; } void doResizeSync() override; bool isWaitingForMoveResizeSync() const override; bool acceptsFocus() const override; private Q_SLOTS: void clientFullScreenChanged(bool fullScreen); private: void init(); + template + void initSurface(T *shellSurface); void requestGeometry(const QRect &rect); void doSetGeometry(const QRect &rect); void createDecoration(const QRect &oldgeom); void destroyClient(); void unmap(); void createWindowId(); void findInternalWindow(); void updateInternalWindowGeometry(); void updateIcon(); void markAsMapped(); void setTransient(); + KWayland::Server::XdgSurfaceV5Interface::States xdgSurfaceStates() const; static void deleteClient(ShellClient *c); KWayland::Server::ShellSurfaceInterface *m_shellSurface; + KWayland::Server::XdgSurfaceV5Interface *m_xdgShellSurface; + KWayland::Server::XdgPopupV5Interface *m_xdgShellPopup; QSize m_clientSize; ClearablePoint m_positionAfterResize; // co-ordinates saved from a requestGeometry call, real geometry will be updated after the next damage event when the client has resized QRect m_geomFsRestore; //size and position of the window before it was set to fullscreen bool m_closing = false; quint32 m_windowId = 0; QWindow *m_internalWindow = nullptr; bool m_unmapped = true; MaximizeMode m_maximizeMode = MaximizeRestore; QRect m_geomMaximizeRestore; // size and position of the window before it was set to maximize NET::WindowType m_windowType = NET::Normal; QPointer m_plasmaShellSurface; QPointer m_qtExtendedSurface; KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr; bool m_userNoBorder = false; bool m_fullScreen = false; bool m_transient = false; bool m_internal; qreal m_opacity = 1.0; }; } Q_DECLARE_METATYPE(KWin::ShellClient*) #endif diff --git a/wayland_server.cpp b/wayland_server.cpp index 3798c1de0..4d11546e7 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -1,516 +1,525 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "wayland_server.h" #include "client.h" #include "platform.h" #include "composite.h" #include "screens.h" #include "shell_client.h" #include "workspace.h" // Client #include #include #include #include #include // Server #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include // Qt #include #include // system #include #include //screenlocker #include using namespace KWayland::Server; namespace KWin { KWIN_SINGLETON_FACTORY(WaylandServer) WaylandServer::WaylandServer(QObject *parent) : QObject(parent) { qRegisterMetaType("KWayland::Server::SurfaceInterface *"); qRegisterMetaType(); } WaylandServer::~WaylandServer() { destroyInputMethodConnection(); } void WaylandServer::destroyInternalConnection() { emit terminatingInternalClientConnection(); if (m_internalConnection.client) { delete m_internalConnection.registry; delete m_internalConnection.shm; dispatch(); m_internalConnection.client->deleteLater(); m_internalConnection.clientThread->quit(); m_internalConnection.clientThread->wait(); delete m_internalConnection.clientThread; m_internalConnection.client = nullptr; m_internalConnection.server->destroy(); } } void WaylandServer::terminateClientConnections() { destroyInternalConnection(); destroyInputMethodConnection(); const auto connections = m_display->connections(); for (auto it = connections.begin(); it != connections.end(); ++it) { (*it)->destroy(); } } + +template +void WaylandServer::createSurface(T *surface) +{ + if (!Workspace::self()) { + // it's possible that a Surface gets created before Workspace is created + return; + } + if (surface->client() == m_xwayland.client) { + // skip Xwayland clients, those are created using standard X11 way + return; + } + if (surface->client() == m_screenLockerClientConnection) { + ScreenLocker::KSldApp::self()->lockScreenShown(); + } + auto client = new ShellClient(surface); + if (auto c = Compositor::self()) { + connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint); + } + if (client->isInternal()) { + m_internalClients << client; + } else { + m_clients << client; + } + if (client->readyForPainting()) { + emit shellClientAdded(client); + } else { + connect(client, &ShellClient::windowShown, this, + [this, client] { + emit shellClientAdded(client); + } + ); + } +} + void WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags) { m_initFlags = flags; m_display = new KWayland::Server::Display(this); if (!socketName.isNull() && !socketName.isEmpty()) { m_display->setSocketName(QString::fromUtf8(socketName)); } m_display->start(); m_compositor = m_display->createCompositor(m_display); m_compositor->create(); connect(m_compositor, &CompositorInterface::surfaceCreated, this, [this] (SurfaceInterface *surface) { // check whether we have a Toplevel with the Surface's id Workspace *ws = Workspace::self(); if (!ws) { // it's possible that a Surface gets created before Workspace is created return; } if (surface->client() != xWaylandConnection()) { // setting surface is only relevat for Xwayland clients return; } auto check = [surface] (const Toplevel *t) { return t->surfaceId() == surface->id(); }; if (Toplevel *t = ws->findToplevel(check)) { t->setSurface(surface); } } ); m_shell = m_display->createShell(m_display); m_shell->create(); - connect(m_shell, &ShellInterface::surfaceCreated, this, - [this] (ShellSurfaceInterface *surface) { - if (!Workspace::self()) { - // it's possible that a Surface gets created before Workspace is created - return; - } - if (surface->client() == m_xwayland.client) { - // skip Xwayland clients, those are created using standard X11 way - return; - } - if (surface->client() == m_screenLockerClientConnection) { - ScreenLocker::KSldApp::self()->lockScreenShown(); - } - auto client = new ShellClient(surface); - if (auto c = Compositor::self()) { - connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint); - } - if (client->isInternal()) { - m_internalClients << client; - } else { - m_clients << client; - } - if (client->readyForPainting()) { - emit shellClientAdded(client); - } else { - connect(client, &ShellClient::windowShown, this, - [this, client] { - emit shellClientAdded(client); - } - ); - } - } - ); + connect(m_shell, &ShellInterface::surfaceCreated, this, &WaylandServer::createSurface); + m_xdgShell = m_display->createXdgShellUnstableVersion5(m_display); + m_xdgShell->create(); + connect(m_xdgShell, &XdgShellV5Interface::surfaceCreated, this, &WaylandServer::createSurface); + // TODO: verify seat and serial + connect(m_xdgShell, &XdgShellV5Interface::popupCreated, this, &WaylandServer::createSurface); m_display->createShm(); m_seat = m_display->createSeat(m_display); m_seat->create(); m_display->createDataDeviceManager(m_display)->create(); m_display->createIdle(m_display)->create(); m_plasmaShell = m_display->createPlasmaShell(m_display); m_plasmaShell->create(); connect(m_plasmaShell, &PlasmaShellInterface::surfaceCreated, [this] (PlasmaShellSurfaceInterface *surface) { if (ShellClient *client = findClient(surface->surface())) { client->installPlasmaShellSurface(surface); } } ); m_qtExtendedSurface = m_display->createQtSurfaceExtension(m_display); m_qtExtendedSurface->create(); connect(m_qtExtendedSurface, &QtSurfaceExtensionInterface::surfaceCreated, [this] (QtExtendedSurfaceInterface *surface) { if (ShellClient *client = findClient(surface->surface())) { client->installQtExtendedSurface(surface); } } ); m_windowManagement = m_display->createPlasmaWindowManagement(m_display); m_windowManagement->create(); m_windowManagement->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Disabled); connect(m_windowManagement, &PlasmaWindowManagementInterface::requestChangeShowingDesktop, this, [] (PlasmaWindowManagementInterface::ShowingDesktopState state) { if (!workspace()) { return; } bool set = false; switch (state) { case PlasmaWindowManagementInterface::ShowingDesktopState::Disabled: set = false; break; case PlasmaWindowManagementInterface::ShowingDesktopState::Enabled: set = true; break; default: Q_UNREACHABLE(); break; } if (set == workspace()->showingDesktop()) { return; } workspace()->setShowingDesktop(set); } ); auto shadowManager = m_display->createShadowManager(m_display); shadowManager->create(); m_display->createDpmsManager(m_display)->create(); m_decorationManager = m_display->createServerSideDecorationManager(m_display); connect(m_decorationManager, &ServerSideDecorationManagerInterface::decorationCreated, this, [this] (ServerSideDecorationInterface *deco) { if (ShellClient *c = findClient(deco->surface())) { c->installServerSideDecoration(deco); } } ); m_decorationManager->create(); m_outputManagement = m_display->createOutputManagement(m_display); connect(m_outputManagement, &OutputManagementInterface::configurationChangeRequested, this, [this](KWayland::Server::OutputConfigurationInterface *config) { kwinApp()->platform()->configurationChangeRequested(config); }); m_display->createSubCompositor(m_display)->create(); } void WaylandServer::initWorkspace() { if (m_windowManagement) { connect(workspace(), &Workspace::showingDesktopChanged, this, [this] (bool set) { using namespace KWayland::Server; m_windowManagement->setShowingDesktopState(set ? PlasmaWindowManagementInterface::ShowingDesktopState::Enabled : PlasmaWindowManagementInterface::ShowingDesktopState::Disabled ); } ); } ScreenLocker::KSldApp::self(); ScreenLocker::KSldApp::self()->setWaylandDisplay(m_display); ScreenLocker::KSldApp::self()->setGreeterEnvironment(kwinApp()->processStartupEnvironment()); ScreenLocker::KSldApp::self()->initialize(); connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::greeterClientConnectionChanged, this, [this] () { m_screenLockerClientConnection = ScreenLocker::KSldApp::self()->greeterClientConnection(); } ); connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::unlocked, this, [this] () { m_screenLockerClientConnection = nullptr; } ); if (m_initFlags.testFlag(InitalizationFlag::LockScreen)) { ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate); } } void WaylandServer::initOutputs() { if (kwinApp()->platform()->handlesOutputs()) { return; } Screens *s = screens(); Q_ASSERT(s); for (int i = 0; i < s->count(); ++i) { OutputInterface *output = m_display->createOutput(m_display); const QRect &geo = s->geometry(i); output->setGlobalPosition(geo.topLeft()); output->setPhysicalSize(geo.size() / 3.8); output->addMode(geo.size()); output->create(); } } int WaylandServer::createXWaylandConnection() { int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { qCWarning(KWIN_CORE) << "Could not create socket"; return -1; } m_xwayland.client = m_display->createClient(sx[0]); m_xwayland.destroyConnection = connect(m_xwayland.client, &KWayland::Server::ClientConnection::disconnected, this, [] { qFatal("Xwayland Connection died"); } ); return sx[1]; } void WaylandServer::destroyXWaylandConnection() { if (!m_xwayland.client) { return; } disconnect(m_xwayland.destroyConnection); m_xwayland.client->destroy(); m_xwayland.client = nullptr; } int WaylandServer::createInputMethodConnection() { int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { qCWarning(KWIN_CORE) << "Could not create socket"; return -1; } m_inputMethodServerConnection = m_display->createClient(sx[0]); return sx[1]; } void WaylandServer::destroyInputMethodConnection() { if (!m_inputMethodServerConnection) { return; } m_inputMethodServerConnection->destroy(); m_inputMethodServerConnection = nullptr; } void WaylandServer::createInternalConnection() { int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { qCWarning(KWIN_CORE) << "Could not create socket"; return; } m_internalConnection.server = m_display->createClient(sx[0]); using namespace KWayland::Client; m_internalConnection.client = new ConnectionThread(); m_internalConnection.client->setSocketFd(sx[1]); m_internalConnection.clientThread = new QThread; m_internalConnection.client->moveToThread(m_internalConnection.clientThread); m_internalConnection.clientThread->start(); connect(m_internalConnection.client, &ConnectionThread::connected, this, [this] { Registry *registry = new Registry(this); EventQueue *eventQueue = new EventQueue(this); eventQueue->setup(m_internalConnection.client); registry->setEventQueue(eventQueue); registry->create(m_internalConnection.client); m_internalConnection.registry = registry; connect(registry, &Registry::shmAnnounced, this, [this] (quint32 name, quint32 version) { m_internalConnection.shm = m_internalConnection.registry->createShmPool(name, version, this); } ); registry->setup(); } ); m_internalConnection.client->initConnection(); } void WaylandServer::removeClient(ShellClient *c) { m_clients.removeAll(c); m_internalClients.removeAll(c); emit shellClientRemoved(c); } void WaylandServer::dispatch() { if (!m_display) { return; } if (m_internalConnection.server) { m_internalConnection.server->flush(); } m_display->dispatchEvents(0); } static ShellClient *findClientInList(const QList &clients, quint32 id) { auto it = std::find_if(clients.begin(), clients.end(), [id] (ShellClient *c) { return c->windowId() == id; } ); if (it == clients.end()) { return nullptr; } return *it; } static ShellClient *findClientInList(const QList &clients, KWayland::Server::SurfaceInterface *surface) { auto it = std::find_if(clients.begin(), clients.end(), [surface] (ShellClient *c) { return c->surface() == surface; } ); if (it == clients.end()) { return nullptr; } return *it; } ShellClient *WaylandServer::findClient(quint32 id) const { if (id == 0) { return nullptr; } if (ShellClient *c = findClientInList(m_clients, id)) { return c; } if (ShellClient *c = findClientInList(m_internalClients, id)) { return c; } return nullptr; } ShellClient *WaylandServer::findClient(SurfaceInterface *surface) const { if (!surface) { return nullptr; } if (ShellClient *c = findClientInList(m_clients, surface)) { return c; } if (ShellClient *c = findClientInList(m_internalClients, surface)) { return c; } return nullptr; } ShellClient *WaylandServer::findClient(QWindow *w) const { if (!w) { return nullptr; } auto it = std::find_if(m_internalClients.constBegin(), m_internalClients.constEnd(), [w] (const ShellClient *c) { return c->internalWindow() == w; } ); if (it != m_internalClients.constEnd()) { return *it; } return nullptr; } quint32 WaylandServer::createWindowId(SurfaceInterface *surface) { auto it = m_clientIds.constFind(surface->client()); quint16 clientId = 0; if (it != m_clientIds.constEnd()) { clientId = it.value(); } else { clientId = createClientId(surface->client()); } Q_ASSERT(clientId != 0); quint32 id = clientId; // TODO: this does not prevent that two surfaces of same client get same id id = (id << 16) | (surface->id() & 0xFFFF); if (findClient(id)) { qCWarning(KWIN_CORE) << "Invalid client windowId generated:" << id; return 0; } return id; } quint16 WaylandServer::createClientId(ClientConnection *c) { auto ids = m_clientIds.values().toSet(); quint16 id = 1; if (!ids.isEmpty()) { for (quint16 i = ids.count() + 1; i >= 1 ; i--) { if (!ids.contains(i)) { id = i; break; } } } Q_ASSERT(!ids.contains(id)); m_clientIds.insert(c, id); connect(c, &ClientConnection::disconnected, this, [this] (ClientConnection *c) { m_clientIds.remove(c); } ); return id; } bool WaylandServer::isScreenLocked() const { return ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::Locked || ScreenLocker::KSldApp::self()->lockState() == ScreenLocker::KSldApp::AcquiringLock; } } diff --git a/wayland_server.h b/wayland_server.h index 5f1918462..500436165 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -1,199 +1,203 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_WAYLAND_SERVER_H #define KWIN_WAYLAND_SERVER_H #include #include class QThread; class QWindow; namespace KWayland { namespace Client { class ConnectionThread; class Registry; class ShmPool; class Surface; } namespace Server { class ClientConnection; class CompositorInterface; class Display; class ShellInterface; class SeatInterface; class ServerSideDecorationManagerInterface; class SurfaceInterface; class OutputInterface; class PlasmaShellInterface; class PlasmaWindowManagementInterface; class QtSurfaceExtensionInterface; class OutputManagementInterface; class OutputConfigurationInterface; +class XdgShellV5Interface; } } namespace KWin { class ShellClient; class AbstractClient; class KWIN_EXPORT WaylandServer : public QObject { Q_OBJECT public: enum class InitalizationFlag { NoOptions = 0x0, LockScreen = 0x1 }; Q_DECLARE_FLAGS(InitalizationFlags, InitalizationFlag) virtual ~WaylandServer(); void init(const QByteArray &socketName = QByteArray(), InitalizationFlags flags = InitalizationFlag::NoOptions); void initOutputs(); void terminateClientConnections(); KWayland::Server::Display *display() { return m_display; } KWayland::Server::CompositorInterface *compositor() { return m_compositor; } KWayland::Server::SeatInterface *seat() { return m_seat; } KWayland::Server::ShellInterface *shell() { return m_shell; } KWayland::Server::PlasmaWindowManagementInterface *windowManagement() { return m_windowManagement; } KWayland::Server::ServerSideDecorationManagerInterface *decorationManager() const { return m_decorationManager; } QList clients() const { return m_clients; } QList internalClients() const { return m_internalClients; } void removeClient(ShellClient *c); ShellClient *findClient(quint32 id) const; ShellClient *findClient(KWayland::Server::SurfaceInterface *surface) const; ShellClient *findClient(QWindow *w) const; /** * @returns file descriptor for Xwayland to connect to. **/ int createXWaylandConnection(); void destroyXWaylandConnection(); /** * @returns file descriptor to the input method server's socket. **/ int createInputMethodConnection(); void destroyInputMethodConnection(); /** * @returns true if screen is locked. **/ bool isScreenLocked() const; void createInternalConnection(); void initWorkspace(); KWayland::Server::ClientConnection *xWaylandConnection() const { return m_xwayland.client; } KWayland::Server::ClientConnection *inputMethodConnection() const { return m_inputMethodServerConnection; } KWayland::Server::ClientConnection *internalConnection() const { return m_internalConnection.server; } KWayland::Server::ClientConnection *screenLockerClientConnection() const { return m_screenLockerClientConnection; } KWayland::Client::ShmPool *internalShmPool() { return m_internalConnection.shm; } KWayland::Client::ConnectionThread *internalClientConection() { return m_internalConnection.client; } KWayland::Client::Registry *internalClientRegistry() { return m_internalConnection.registry; } void dispatch(); quint32 createWindowId(KWayland::Server::SurfaceInterface *surface); Q_SIGNALS: void shellClientAdded(KWin::ShellClient*); void shellClientRemoved(KWin::ShellClient*); void terminatingInternalClientConnection(); private: quint16 createClientId(KWayland::Server::ClientConnection *c); void destroyInternalConnection(); void configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config); + template + void createSurface(T *surface); KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositor = nullptr; KWayland::Server::SeatInterface *m_seat = nullptr; KWayland::Server::ShellInterface *m_shell = nullptr; + KWayland::Server::XdgShellV5Interface *m_xdgShell = nullptr; KWayland::Server::PlasmaShellInterface *m_plasmaShell = nullptr; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagement = nullptr; KWayland::Server::QtSurfaceExtensionInterface *m_qtExtendedSurface = nullptr; KWayland::Server::ServerSideDecorationManagerInterface *m_decorationManager = nullptr; KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr; struct { KWayland::Server::ClientConnection *client = nullptr; QMetaObject::Connection destroyConnection; } m_xwayland; KWayland::Server::ClientConnection *m_inputMethodServerConnection = nullptr; KWayland::Server::ClientConnection *m_screenLockerClientConnection = nullptr; struct { KWayland::Server::ClientConnection *server = nullptr; KWayland::Client::ConnectionThread *client = nullptr; QThread *clientThread = nullptr; KWayland::Client::Registry *registry = nullptr; KWayland::Client::ShmPool *shm = nullptr; } m_internalConnection; QList m_clients; QList m_internalClients; QHash m_clientIds; InitalizationFlags m_initFlags; KWIN_SINGLETON(WaylandServer) }; inline WaylandServer *waylandServer() { return WaylandServer::self(); } } // namespace KWin #endif