diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt --- a/ksmserver/CMakeLists.txt +++ b/ksmserver/CMakeLists.txt @@ -7,14 +7,14 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(logout-greeter) +add_subdirectory(switchuser-greeter) ########### next target ############### set(ksmserver_KDEINIT_SRCS ksmserver_debug.cpp main.cpp server.cpp - switchuserdialog.cpp legacy.cpp startup.cpp autostart.cpp diff --git a/ksmserver/config-ksmserver.h.cmake b/ksmserver/config-ksmserver.h.cmake --- a/ksmserver/config-ksmserver.h.cmake +++ b/ksmserver/config-ksmserver.h.cmake @@ -1,5 +1,6 @@ /* Define to 1 if you have the `_IceTransNoListen' function. */ #cmakedefine HAVE__ICETRANSNOLISTEN 1 #define LOGOUT_GREETER_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/ksmserver-logout-greeter" +#define SWITCHUSER_GREETER_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/ksmserver-switchuser-greeter" #define KWIN_BIN "${KWIN_BIN}" diff --git a/ksmserver/server.cpp b/ksmserver/server.cpp --- a/ksmserver/server.cpp +++ b/ksmserver/server.cpp @@ -1083,13 +1083,20 @@ void KSMServer::openSwitchUserDialog() { - KDisplayManager dm; - if (!dm.isSwitchable()) { + if (dialogActive) { return; } - QScopedPointer dlg(new KSMSwitchUserDialog(&dm)); - dlg->exec(); + QProcess *p = new QProcess(this); + p->setProgram(QStringLiteral(SWITCHUSER_GREETER_BIN)); + + connect(p, static_cast(&QProcess::finished), this, [this, p] { + p->deleteLater(); + dialogActive = false; + }); + + dialogActive = true; + p->start(); } void KSMServer::runShutdownScripts() diff --git a/ksmserver/switchuser-greeter/CMakeLists.txt b/ksmserver/switchuser-greeter/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/ksmserver/switchuser-greeter/CMakeLists.txt @@ -0,0 +1,15 @@ +set(KSMSERVER_SWITCHUSER_GREETER_SRCS main.cpp ../switchuserdialog.cpp ../ksmserver_debug.cpp) +add_executable(ksmserver-switchuser-greeter ${KSMSERVER_SWITCHUSER_GREETER_SRCS}) +target_link_libraries(ksmserver-switchuser-greeter + PW::KWorkspace + Qt5::Quick + Qt5::X11Extras + KF5::Declarative + KF5::IconThemes + KF5::I18n + KF5::Package + KF5::WaylandClient + KF5::WindowSystem + ${X11_LIBRARIES} +) +install(TARGETS ksmserver-switchuser-greeter DESTINATION ${KDE_INSTALL_LIBEXECDIR}) diff --git a/ksmserver/switchuser-greeter/main.cpp b/ksmserver/switchuser-greeter/main.cpp new file mode 100644 --- /dev/null +++ b/ksmserver/switchuser-greeter/main.cpp @@ -0,0 +1,147 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright 2016 Martin Graesslin +Copyright 2016 Kai Uwe Broulik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include +#include + +#include "../switchuserdialog.h" + +#include +#include + +#include +#include +#include + +#include + +class Greeter : public QObject +{ + Q_OBJECT +public: + Greeter(); + ~Greeter() override; + + void init(); + + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + void adoptScreen(QScreen *screen); + void rejected(); + void setupWaylandIntegration(); + + QVector m_dialogs; + KWayland::Client::PlasmaShell *m_waylandPlasmaShell; + KDisplayManager m_displayManager; +}; + +Greeter::Greeter() + : QObject() + , m_waylandPlasmaShell(nullptr) +{ +} + +Greeter::~Greeter() +{ + qDeleteAll(m_dialogs); +} + +void Greeter::setupWaylandIntegration() +{ + if (!KWindowSystem::isPlatformWayland()) { + return; + } + using namespace KWayland::Client; + ConnectionThread *connection = ConnectionThread::fromApplication(this); + if (!connection) { + return; + } + Registry *registry = new Registry(this); + registry->create(connection); + connect(registry, &Registry::plasmaShellAnnounced, this, + [this, registry] (quint32 name, quint32 version) { + m_waylandPlasmaShell = registry->createPlasmaShell(name, version, this); + } + ); + registry->setup(); + connection->roundtrip(); +} + +void Greeter::init() +{ + setupWaylandIntegration(); + foreach (QScreen *screen, qApp->screens()) { + adoptScreen(screen); + } + connect(qApp, &QGuiApplication::screenAdded, this, &Greeter::adoptScreen); +} + +void Greeter::adoptScreen(QScreen* screen) +{ + KSMSwitchUserDialog *w = new KSMSwitchUserDialog(&m_displayManager, m_waylandPlasmaShell); + w->installEventFilter(this); + m_dialogs << w; + + QObject::connect(screen, &QObject::destroyed, w, [w, this] { + m_dialogs.removeOne(w); + w->deleteLater(); + }); + connect(w, &KSMSwitchUserDialog::dismissed, qApp, &QCoreApplication::quit); + w->setScreen(screen); + w->setGeometry(screen->geometry()); + w->init(); +} + +bool Greeter::eventFilter(QObject *watched, QEvent *event) +{ + if (qobject_cast(watched)) { + if (event->type() == QEvent::MouseButtonPress) { + // check that the position is on no window + QMouseEvent *me = static_cast(event); + for (auto it = m_dialogs.constBegin(); it != m_dialogs.constEnd(); ++it) { + if ((*it)->geometry().contains(me->globalPos())) { + return false; + } + } + // click outside, close + qApp->quit(); + } + } + return false; +} + +int main(int argc, char *argv[]) +{ + QQuickWindow::setDefaultAlphaBuffer(true); + QGuiApplication app(argc, argv); + + Greeter greeter; + greeter.init(); + + return app.exec(); +} + +#include "main.moc" diff --git a/ksmserver/switchuserdialog.h b/ksmserver/switchuserdialog.h --- a/ksmserver/switchuserdialog.h +++ b/ksmserver/switchuserdialog.h @@ -25,22 +25,39 @@ class KDisplayManager; +namespace KWayland +{ +namespace Client +{ +class PlasmaShell; +class PlasmaShellSurface; +} +} + class KSMSwitchUserDialog : public QQuickView { Q_OBJECT public: - explicit KSMSwitchUserDialog(KDisplayManager *dm, QWindow *parent = nullptr); + explicit KSMSwitchUserDialog(KDisplayManager *dm, KWayland::Client::PlasmaShell *plasmaShell = nullptr, QWindow *parent = nullptr); ~KSMSwitchUserDialog() override = default; - void exec(); + void init(); signals: void dismissed(); +protected: + bool event(QEvent *e) override; + private: + void setupWaylandIntegration(); + KDisplayManager *m_displayManager = nullptr; + KWayland::Client::PlasmaShell *m_waylandPlasmaShell; + KWayland::Client::PlasmaShellSurface *m_shellSurface = nullptr; + }; #endif // SWITCHUSERDIALOG_H diff --git a/ksmserver/switchuserdialog.cpp b/ksmserver/switchuserdialog.cpp --- a/ksmserver/switchuserdialog.cpp +++ b/ksmserver/switchuserdialog.cpp @@ -19,43 +19,47 @@ */ #include "switchuserdialog.h" +#include "ksmserver_debug.h" #include -#include -#include #include -#include -#include +#include #include #include #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include +#include +#include + #include #include -KSMSwitchUserDialog::KSMSwitchUserDialog(KDisplayManager *dm, QWindow *parent) +KSMSwitchUserDialog::KSMSwitchUserDialog(KDisplayManager *dm, KWayland::Client::PlasmaShell *plasmaShell, QWindow *parent) : QQuickView(parent) , m_displayManager(dm) + , m_waylandPlasmaShell(plasmaShell) { setClearBeforeRendering(true); setColor(QColor(Qt::transparent)); setFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint); + setResizeMode(QQuickView::SizeRootObjectToView); + QPoint globalPosition(QCursor::pos()); foreach (QScreen *s, QGuiApplication::screens()) { if (s->geometry().contains(globalPosition)) { @@ -66,19 +70,26 @@ // Qt doesn't set this on unmanaged windows //FIXME: or does it? - XChangeProperty( QX11Info::display(), winId(), - XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False ), XA_STRING, 8, PropModeReplace, - (unsigned char *)"logoutdialog", strlen( "logoutdialog" )); - - - rootContext()->setContextProperty(QStringLiteral("screenGeometry"), screen()->geometry()); - - setModality(Qt::ApplicationModal); + if (KWindowSystem::isPlatformX11()) { + XChangeProperty( QX11Info::display(), winId(), + XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False ), XA_STRING, 8, PropModeReplace, + (unsigned char *)"logoutdialog", strlen( "logoutdialog" )); + + XClassHint classHint; + classHint.res_name = const_cast("ksmserver"); + classHint.res_class = const_cast("ksmserver"); + XSetClassHint(QX11Info::display(), winId(), &classHint); + } KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); - //kdeclarative.initialize(); + kdeclarative.initialize(); kdeclarative.setupBindings(); +} + +void KSMSwitchUserDialog::init() +{ + rootContext()->setContextProperty(QStringLiteral("screenGeometry"), screen()->geometry()); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage("Plasma/LookAndFeel"); KConfigGroup cg(KSharedConfig::openConfig("kdeglobals"), "KDE"); @@ -96,24 +107,59 @@ return; } - setPosition(screen()->virtualGeometry().center().x() - width() / 2, - screen()->virtualGeometry().center().y() - height() / 2); - if (!errors().isEmpty()) { qCWarning(KSMSERVER) << errors(); } connect(rootObject(), SIGNAL(dismissed()), this, SIGNAL(dismissed())); - show(); + connect(screen(), &QScreen::geometryChanged, this, [this] { + setGeometry(screen()->geometry()); + }); + + QQuickView::show(); requestActivate(); - KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); + KWindowSystem::setState(winId(), NET::SkipTaskbar|NET::SkipPager); + + setKeyboardGrabEnabled(true); +} + +bool KSMSwitchUserDialog::event(QEvent *e) +{ + if (e->type() == QEvent::PlatformSurface) { + if (auto pe = dynamic_cast(e)) { + switch (pe->surfaceEventType()) { + case QPlatformSurfaceEvent::SurfaceCreated: + setupWaylandIntegration(); + KWindowEffects::enableBlurBehind(winId(), true); + break; + case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: + delete m_shellSurface; + m_shellSurface = nullptr; + break; + } + } + } + return QQuickView::event(e); } -void KSMSwitchUserDialog::exec() +void KSMSwitchUserDialog::setupWaylandIntegration() { - QEventLoop loop; - connect(this, &KSMSwitchUserDialog::dismissed, &loop, &QEventLoop::quit); - loop.exec(); + if (m_shellSurface) { + // already setup + return; + } + using namespace KWayland::Client; + if (!m_waylandPlasmaShell) { + return; + } + Surface *s = Surface::fromWindow(this); + if (!s) { + return; + } + m_shellSurface = m_waylandPlasmaShell->createSurface(s, this); + // TODO: set a proper window type to indicate to KWin that this is the logout dialog + // maybe we need a dedicated type for it? + m_shellSurface->setPosition(geometry().topLeft()); }