Notify effects when the screen gets locked

EffectsHandlerImpl starts to monitor DBus for the screen being locked and
provides this information to the Effect system by allowing them to ask
whether the screen is currently locked and by emitting a signal when the
screen gets locked/unlocked.

This information is needed to ensure that no private data is shown on the
screen. The following effects are adjusted:
* taskbar thumbnails
* thumbnail aside
* mouse mark
* screen shot

BUG: 255712
FIXED-IN: 4.11
REVIEW: 108670
This commit is contained in:
Martin Gräßlin 2013-01-30 13:07:59 +01:00
parent beb00478e1
commit 10c07080bb
11 changed files with 182 additions and 6 deletions

View file

@ -176,6 +176,8 @@ qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Effects.xml effects.h KWin:
qt4_add_dbus_interface( kwin_KDEINIT_SRCS
${KDEBASE_WORKSPACE_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml ksmserver_interface)
qt4_add_dbus_interface( kwin_KDEINIT_SRCS
${KDEBASE_WORKSPACE_SOURCE_DIR}/ksmserver/screenlocker/dbus/org.freedesktop.ScreenSaver.xml screenlocker_interface)
qt4_add_resources( kwin_KDEINIT_SRCS resources.qrc )

View file

@ -45,6 +45,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QFile>
#include <QtCore/QFutureWatcher>
#include <QtCore/QtConcurrentRun>
#include <QDBusServiceWatcher>
#include <QDBusPendingCallWatcher>
#include "kdebug.h"
#include "klibrary.h"
@ -60,10 +62,104 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "composite.h"
#include "xcbutils.h"
// dbus generated
#include "screenlocker_interface.h"
namespace KWin
{
static const QString SCREEN_LOCKER_SERVICE_NAME = QString("org.freedesktop.ScreenSaver");
ScreenLockerWatcher::ScreenLockerWatcher(QObject *parent)
: QObject(parent)
, m_interface(NULL)
, m_serviceWatcher(new QDBusServiceWatcher(this))
, m_locked(false)
{
connect(m_serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(serviceOwnerChanged(QString,QString,QString)));
m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
m_serviceWatcher->addWatchedService(SCREEN_LOCKER_SERVICE_NAME);
// check whether service is registered
QFutureWatcher<QDBusReply<bool> > *watcher = new QFutureWatcher<QDBusReply<bool> >(this);
connect(watcher, SIGNAL(finished()), SLOT(serviceRegisteredQueried()));
connect(watcher, SIGNAL(canceled()), watcher, SLOT(deleteLater()));
watcher->setFuture(QtConcurrent::run(QDBusConnection::sessionBus().interface(),
&QDBusConnectionInterface::isServiceRegistered,
SCREEN_LOCKER_SERVICE_NAME));
}
ScreenLockerWatcher::~ScreenLockerWatcher()
{
}
void ScreenLockerWatcher::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
{
Q_UNUSED(oldOwner)
if (serviceName != SCREEN_LOCKER_SERVICE_NAME) {
return;
}
delete m_interface;
m_interface = NULL;
m_locked = false;
if (!newOwner.isEmpty()) {
m_interface = new OrgFreedesktopScreenSaverInterface(newOwner, QString(), QDBusConnection::sessionBus(), this);
connect(m_interface, SIGNAL(ActiveChanged(bool)), SLOT(setLocked(bool)));
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(m_interface->GetActive(), this);
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(activeQueried(QDBusPendingCallWatcher*)));
}
}
void ScreenLockerWatcher::serviceRegisteredQueried()
{
QFutureWatcher<QDBusReply<bool> > *watcher = dynamic_cast<QFutureWatcher<QDBusReply<bool> > *>(sender());
if (!watcher) {
return;
}
const QDBusReply<bool> &reply = watcher->result();
if (reply.isValid() && reply.value()) {
QFutureWatcher<QDBusReply<QString> > *ownerWatcher = new QFutureWatcher<QDBusReply<QString> >(this);
connect(ownerWatcher, SIGNAL(finished()), SLOT(serviceOwnerQueried()));
connect(ownerWatcher, SIGNAL(canceled()), ownerWatcher, SLOT(deleteLater()));
ownerWatcher->setFuture(QtConcurrent::run(QDBusConnection::sessionBus().interface(),
&QDBusConnectionInterface::serviceOwner,
SCREEN_LOCKER_SERVICE_NAME));
}
watcher->deleteLater();
}
void ScreenLockerWatcher::serviceOwnerQueried()
{
QFutureWatcher<QDBusReply<QString> > *watcher = dynamic_cast<QFutureWatcher<QDBusReply<QString> > *>(sender());
if (!watcher) {
return;
}
const QDBusReply<QString> reply = watcher->result();
if (reply.isValid()) {
serviceOwnerChanged(SCREEN_LOCKER_SERVICE_NAME, QString(), reply.value());
}
watcher->deleteLater();
}
void ScreenLockerWatcher::activeQueried(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply<bool> reply = *watcher;
if (!reply.isError()) {
setLocked(reply.value());
}
watcher->deleteLater();
}
void ScreenLockerWatcher::setLocked(bool activated)
{
if (m_locked == activated) {
return;
}
m_locked = activated;
emit locked(m_locked);
}
//---------------------
// Static
@ -112,6 +208,7 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
, mouse_poll_ref_count(0)
, m_compositor(compositor)
, m_scene(scene)
, m_screenLockerWatcher(new ScreenLockerWatcher(this))
{
new EffectsAdaptor(this);
QDBusConnection dbus = QDBusConnection::sessionBus();
@ -144,6 +241,7 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
#ifdef KWIN_BUILD_SCREENEDGES
connect(ScreenEdges::self(), SIGNAL(approaching(ElectricBorder,qreal,QRect)), SIGNAL(screenEdgeApproaching(ElectricBorder,qreal,QRect)));
#endif
connect(m_screenLockerWatcher, SIGNAL(locked(bool)), SIGNAL(screenLockingChanged(bool)));
// connect all clients
foreach (Client *c, ws->clientList()) {
setupClientConnections(c);
@ -1536,6 +1634,11 @@ QString EffectsHandlerImpl::supportInformation(const QString &name) const
return QString();
}
bool EffectsHandlerImpl::isScreenLocked() const
{
return m_screenLockerWatcher->isLocked();
}
//****************************************
// EffectWindowImpl
//****************************************

View file

@ -30,8 +30,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QHash>
#include <Plasma/FrameSvg>
class QDBusPendingCallWatcher;
class QDBusServiceWatcher;
class KService;
class OrgFreedesktopScreenSaverInterface;
namespace KWin
@ -44,6 +46,7 @@ class Client;
class Compositor;
class Deleted;
class Unmanaged;
class ScreenLockerWatcher;
class EffectsHandlerImpl : public EffectsHandler
{
@ -160,6 +163,7 @@ public:
virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const;
virtual QVariant kwinOption(KWinOption kwopt);
virtual bool isScreenLocked() const;
// internal (used by kwin core or compositing code)
void startPaint();
@ -241,6 +245,7 @@ private:
Compositor *m_compositor;
Scene *m_scene;
QList< InputWindowPair > input_windows;
ScreenLockerWatcher *m_screenLockerWatcher;
};
class EffectWindowImpl : public EffectWindow
@ -386,6 +391,29 @@ private:
GLShader* m_shader;
};
class ScreenLockerWatcher : public QObject
{
Q_OBJECT
public:
explicit ScreenLockerWatcher(QObject *parent = 0);
virtual ~ScreenLockerWatcher();
bool isLocked() const {
return m_locked;
}
Q_SIGNALS:
void locked(bool locked);
private Q_SLOTS:
void setLocked(bool activated);
void activeQueried(QDBusPendingCallWatcher *watcher);
void serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner);
void serviceRegisteredQueried();
void serviceOwnerQueried();
private:
OrgFreedesktopScreenSaverInterface *m_interface;
QDBusServiceWatcher *m_serviceWatcher;
bool m_locked;
};
inline
QList<EffectWindow*> EffectsHandlerImpl::elevatedWindows() const
{

View file

@ -59,6 +59,7 @@ MouseMarkEffect::MouseMarkEffect()
connect(a, SIGNAL(triggered(bool)), this, SLOT(clearLast()));
connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)),
this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)));
connect(effects, SIGNAL(screenLockingChanged(bool)), SLOT(screenLockingChanged(bool)));
reconfigure(ReconfigureAll);
arrow_start = NULL_POINT;
effects->startMousePolling(); // We require it to detect activation as well
@ -239,9 +240,22 @@ MouseMarkEffect::Mark MouseMarkEffect::createArrow(QPoint arrow_start, QPoint ar
return ret;
}
void MouseMarkEffect::screenLockingChanged(bool locked)
{
if (!marks.isEmpty() || !drawing.isEmpty()) {
effects->addRepaintFull();
}
// disable mouse polling while screen is locked.
if (locked) {
effects->stopMousePolling();
} else {
effects->startMousePolling();
}
}
bool MouseMarkEffect::isActive() const
{
return !marks.isEmpty() || !drawing.isEmpty();
return (!marks.isEmpty() || !drawing.isEmpty()) && !effects->isScreenLocked();
}

View file

@ -56,6 +56,7 @@ private slots:
void slotMouseChanged(const QPoint& pos, const QPoint& old,
Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers);
void screenLockingChanged(bool locked);
private:
typedef QVector< QPoint > Mark;
static Mark createArrow(QPoint arrow_start, QPoint arrow_end);

View file

@ -353,7 +353,7 @@ void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h)
bool ScreenShotEffect::isActive() const
{
return m_scheduledScreenshot != NULL;
return m_scheduledScreenshot != NULL && !effects->isScreenLocked();
}
void ScreenShotEffect::windowClosed( EffectWindow* w )

View file

@ -41,6 +41,7 @@ TaskbarThumbnailEffect::TaskbarThumbnailEffect()
connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*)));
connect(effects, SIGNAL(windowDamaged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowDamaged(KWin::EffectWindow*,QRect)));
connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long)));
connect(effects, SIGNAL(screenLockingChanged(bool)), SLOT(screenLockingChanged()));
}
TaskbarThumbnailEffect::~TaskbarThumbnailEffect()
@ -147,9 +148,16 @@ void TaskbarThumbnailEffect::slotPropertyNotify(EffectWindow* w, long a)
}
}
void TaskbarThumbnailEffect::screenLockingChanged()
{
foreach (EffectWindow *window, thumbnails.uniqueKeys()) {
window->addRepaintFull();
}
}
bool TaskbarThumbnailEffect::isActive() const
{
return !thumbnails.isEmpty();
return !thumbnails.isEmpty() && !effects->isScreenLocked();
}
} // namespace

View file

@ -45,6 +45,7 @@ public Q_SLOTS:
void slotWindowDeleted(KWin::EffectWindow *w);
void slotWindowDamaged(KWin::EffectWindow* w, const QRect& damage);
void slotPropertyNotify(KWin::EffectWindow *w, long atom);
void screenLockingChanged();
private:
struct Data {
Window window; // thumbnail of this window

View file

@ -42,6 +42,7 @@ ThumbnailAsideEffect::ThumbnailAsideEffect()
connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*)));
connect(effects, SIGNAL(windowGeometryShapeChanged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(KWin::EffectWindow*,QRect)));
connect(effects, SIGNAL(windowDamaged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowDamaged(KWin::EffectWindow*,QRect)));
connect(effects, SIGNAL(screenLockingChanged(bool)), SLOT(repaintAll()));
reconfigure(ReconfigureAll);
}
@ -182,7 +183,7 @@ void ThumbnailAsideEffect::repaintAll()
bool ThumbnailAsideEffect::isActive() const
{
return !windows.isEmpty();
return !windows.isEmpty() && !effects->isScreenLocked();
}
} // namespace

View file

@ -68,11 +68,11 @@ private slots:
void slotWindowGeometryShapeChanged(KWin::EffectWindow *w, const QRect &old);
void slotWindowDamaged(KWin::EffectWindow* w, const QRect& damage);
virtual bool isActive() const;
void repaintAll();
private:
void addThumbnail(EffectWindow* w);
void removeThumbnail(EffectWindow* w);
void arrange();
void repaintAll();
struct Data {
EffectWindow* window; // the same like the key in the hash (makes code simpler)
int index;

View file

@ -875,6 +875,18 @@ public:
**/
virtual void reloadEffect(Effect *effect) = 0;
/**
* Whether the screen is currently considered as locked.
* Note for technical reasons this is not always possible to detect. The screen will only
* be considered as locked if the screen locking process implements the
* org.freedesktop.ScreenSaver interface.
*
* @returns @c true if the screen is currently locked, @c false otherwise
* @see screenLockingChanged
* @since 4.11
**/
virtual bool isScreenLocked() const = 0;
/**
* Sends message over DCOP to reload given effect.
* @param effectname effect's name without "kwin4_effect_" prefix.
@ -1155,6 +1167,12 @@ Q_SIGNALS:
* @since 4.9
*/
void activityRemoved(const QString &id);
/**
* This signal is emitted when the screen got locked or unlocked.
* @param locked @c true if the screen is now locked, @c false if it is now unlocked
* @since 4.11
**/
void screenLockingChanged(bool locked);
/**
* This signels is emitted when ever the stacking order is change, ie. a window is risen