diff --git a/kwin/CMakeLists.txt b/kwin/CMakeLists.txt index 45651c1..1405aa6 100644 --- a/kwin/CMakeLists.txt +++ b/kwin/CMakeLists.txt @@ -46,21 +46,29 @@ OPTION(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON) OPTION(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON) OPTION(KWIN_MOBILE_EFFECTS "Only build effects relevant for mobile devices" OFF) +# screensaver/locking stuff: +macro_bool_to_01(X11_Xscreensaver_FOUND HAVE_XSCREENSAVER) +macro_log_feature(HAVE_XSCREENSAVER "libxss" "XScreenSaver Library" "http://www.x.org/" FALSE "" "Needed to enable screensaver status check") + # for things that are also used by kwin libraries configure_file(libkwineffects/kwinconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h ) # for kwin internal things configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h ) - +# for screensaver/locking +configure_file(screenlocker/config-xautolock.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/screenlocker/config-xautolock.h) ########### global ############### include_directories( ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/screenlocker ${CMAKE_CURRENT_SOURCE_DIR}/libkwineffects ${CMAKE_CURRENT_SOURCE_DIR}/libkdecorations ${CMAKE_CURRENT_SOURCE_DIR}/effects ${CMAKE_CURRENT_SOURCE_DIR}/tabbox + ${CMAKE_CURRENT_SOURCE_DIR}/screenlocker/ + ${CMAKE_CURRENT_SOURCE_DIR}/screenlocker/screensaver ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kephal ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kworkspace ) @@ -159,8 +167,23 @@ set(kwin_KDEINIT_SRCS # floating tilinglayouts/floating/floating.cpp + + # screensaver activation + screenlocker/screenlocker.cpp + screenlocker/screensaver/saverengine.cpp + screenlocker/screensaver/xautolock.cpp + screenlocker/screensaver/xautolock_diy.c + screenlocker/screensaver/xautolock_engine.c ) +set(screensaver_dbusXML screenlocker/dbus/org.freedesktop.ScreenSaver.xml) +set(kscreensaver_dbusXML screenlocker/dbus/org.kde.screensaver.xml) + +kde4_add_kcfg_files( kwin_KDEINIT_SRCS screenlocker/kcfg/kscreensaversettings.kcfgc ) + +qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS ${screensaver_dbusXML} saverengine.h SaverEngine) +qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kscreensaver_dbusXML} saverengine.h SaverEngine kscreensaveradaptor KScreenSaverAdaptor) + qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWin::Workspace ) qt4_add_dbus_interface( kwin_KDEINIT_SRCS @@ -172,6 +195,10 @@ kde4_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS}) target_link_libraries(kdeinit_kwin ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${QT_QTXML_LIBRARY} ${QT_QTSCRIPT_LIBRARY} kephal kworkspace kdecorations kwineffects ${X11_LIBRARIES}) +if(X11_Xscreensaver_FOUND) + target_link_libraries(kdeinit_kwin ${kwinLibs} ${X11_Xscreensaver_LIB}) +endif(X11_Xscreensaver_FOUND) + if(OPENGL_FOUND AND NOT KWIN_HAVE_OPENGLES_COMPOSITING) add_subdirectory(opengltest) target_link_libraries(kdeinit_kwin ${OPENGL_gl_LIBRARY}) @@ -224,6 +251,8 @@ install(TARGETS kwinnvidiahack ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_ install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES kwin.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kwin ) install( FILES org.kde.KWin.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) +install( FILES ${screensaver_dbusXML} DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) +install( FILES ${kscreensaver_dbusXML} DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) install( FILES tabbox/DefaultTabBoxLayouts.xml DESTINATION ${DATA_INSTALL_DIR}/kwin ) kde4_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/kwin/effects.cpp b/kwin/effects.cpp index f5863fc0..d537fa5 100644 --- a/kwin/effects.cpp +++ b/kwin/effects.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #include "tabbox.h" #include "workspace.h" #include "kwinglutils.h" +#include "screenlocker/screenlocker.h" #include @@ -96,6 +97,7 @@ EffectsHandlerImpl::EffectsHandlerImpl(CompositingType type) , next_window_quad_type(EFFECT_QUAD_TYPE_START) , mouse_poll_ref_count(0) , current_paint_effectframe(0) + , m_activeScreenLockEffect(NULL) { Workspace *ws = Workspace::self(); connect(ws, SIGNAL(currentDesktopChanged(int)), this, SLOT(slotDesktopChanged(int))); @@ -1189,6 +1191,17 @@ EffectFrame* EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool static return new EffectFrameImpl(style, staticSize, position, alignment); } + +QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt) +{ + switch (kwopt) + { + case CloseButtonCorner: + return Workspace::self()->decorationCloseButtonCorner(); + } + return QVariant(); // an invalid one +} + void EffectsHandlerImpl::slotShowOutline(const QRect& geometry) { emit showOutline(geometry); @@ -1199,6 +1212,37 @@ void EffectsHandlerImpl::slotHideOutline() emit hideOutline(); } +static bool s_prepareLock = false; +bool EffectsHandlerImpl::lockScreen() +{ + s_prepareLock = true; + emit requestScreenLock(); + s_prepareLock = false; + return isScreenLockerReferenced(); +} + +void EffectsHandlerImpl::refScreenLocker(Effect* lockEffect) +{ + if (m_activeScreenLockEffect || !s_prepareLock) { + return; + } + m_activeScreenLockEffect = lockEffect; +} + +void EffectsHandlerImpl::unrefScreenLocker(Effect* lockEffect) +{ + if (m_activeScreenLockEffect != lockEffect) { + return; + } + m_activeScreenLockEffect = NULL; + Workspace::self()->screenLocker()->unlock(); +} + +bool EffectsHandlerImpl::isScreenLockerReferenced() const +{ + return m_activeScreenLockEffect != NULL; +} + //**************************************** // EffectWindowImpl //**************************************** diff --git a/kwin/effects.h b/kwin/effects.h index abff777..3a626b1 100644 --- a/kwin/effects.h +++ b/kwin/effects.h @@ -151,6 +151,12 @@ public: virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const; + virtual QVariant kwinOption(KWinOption kwopt); + + virtual void refScreenLocker(Effect *lockEffect); + virtual void unrefScreenLocker(Effect *lockEffect); + virtual bool isScreenLockerReferenced() const; + // internal (used by kwin core or compositing code) void startPaint(); bool borderActivated(ElectricBorder border); @@ -167,6 +173,13 @@ public: QList elevatedWindows() const; + /** + * Passes the request to lock the screen to the effects and returns whether an effect + * will handle the screen locking. + * @returns @c true in case an effect handles the screen locking, @c false otherwise. + **/ + bool lockScreen(); + public Q_SLOTS: void slotClientGroupItemSwitched(EffectWindow* from, EffectWindow* to); void slotClientGroupItemAdded(EffectWindow* from, EffectWindow* to); @@ -209,6 +222,15 @@ protected: int next_window_quad_type; int mouse_poll_ref_count; int current_paint_effectframe; + +private: + QList< Effect* > m_activeEffects; + QList< Effect* >::iterator m_currentDrawWindowIterator; + QList< Effect* >::iterator m_currentPaintWindowIterator; + QList< Effect* >::iterator m_currentPaintEffectFrameIterator; + QList< Effect* >::iterator m_currentPaintScreenIterator; + QList< Effect* >::iterator m_currentBuildQuadsIterator; + Effect *m_activeScreenLockEffect; }; class EffectWindowImpl : public EffectWindow diff --git a/kwin/effects/CMakeLists.txt b/kwin/effects/CMakeLists.txt index 0312ac2..fa47082 100644 --- a/kwin/effects/CMakeLists.txt +++ b/kwin/effects/CMakeLists.txt @@ -7,7 +7,7 @@ kde4_no_enable_final(kwineffects) # Adds effect plugin with given name. Sources are given after the name macro( KWIN4_ADD_EFFECT name ) kde4_add_plugin( kwin4_effect_${name} ${ARGN} ) - target_link_libraries( kwin4_effect_${name} kwineffects ${KDE4_KDEUI_LIBS} kephal ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB}) + target_link_libraries( kwin4_effect_${name} kwineffects ${KDE4_KDEUI_LIBS} kephal ${KDE4_PLASMA_LIBS} ${X11_Xfixes_LIB} ${X11_Xcursor_LIB} ${QT_QTDECLARATIVE_LIBRARY} kdeclarative) if (X11_Xfixes_FOUND) target_link_libraries(kwin4_effect_${name} ${X11_Xfixes_LIB}) endif (X11_Xfixes_FOUND) @@ -62,6 +62,7 @@ include( fade/CMakeLists.txt ) include( login/CMakeLists.txt ) include( outline/CMakeLists.txt ) include( presentwindows/CMakeLists.txt ) +include( screenlocker/CMakeLists.txt ) include( slidingpopups/CMakeLists.txt ) include( taskbarthumbnail/CMakeLists.txt ) diff --git a/kwin/effects/presentwindows/presentwindows.cpp b/kwin/effects/presentwindows/presentwindows.cpp index a0e8306..bea897b 100755 --- a/kwin/effects/presentwindows/presentwindows.cpp +++ b/kwin/effects/presentwindows/presentwindows.cpp @@ -295,9 +295,12 @@ void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &d } else if (winData->opacity != 1.0) data.setTranslucent(); + const bool isInMotion = m_motionManager.isManaging(w); // Calculate window's brightness if (w == m_highlightedWindow || w == m_closeWindow || !m_activated) winData->highlight = qMin(1.0, winData->highlight + time / m_fadeDuration); + else if (!isInMotion && w->isDesktop()) + winData->highlight = 0.3; else winData->highlight = qMax(0.0, winData->highlight - time / m_fadeDuration); @@ -317,7 +320,7 @@ void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &d if (w->isDesktop() && !w->isOnCurrentDesktop()) w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); - if (m_motionManager.isManaging(w)) + if (isInMotion) data.setTransformed(); // We will be moving this window } effects->prePaintWindow(w, data, time); @@ -333,15 +336,50 @@ void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region return; } + mask |= PAINT_WINDOW_LANCZOS; // Apply opacity and brightness data.opacity *= winData->opacity; - data.brightness *= interpolate(0.7, 1.0, winData->highlight); + data.brightness *= interpolate(0.40, 1.0, winData->highlight); if (m_motionManager.isManaging(w)) { + if (w->isDesktop()) + effects->paintWindow(w, mask, region, data); m_motionManager.apply(w, data); + QRect rect = m_motionManager.transformedGeometry(w).toRect(); + + if (winData->highlight > 0.0) { + // scale the window (interpolated by the highlight level) to at least 105% or to cover 1/16 of the screen size - yet keep it in screen bounds + QRect area = effects->clientArea(FullScreenArea, w); + QSizeF effSize(w->width()*data.xScale, w->height()*data.yScale); + float tScale = sqrt((area.width()*area.height()) / (16.0*effSize.width()*effSize.height())); + if (tScale < 1.05) + tScale = 1.05; + if (effSize.width()*tScale > area.width()) + tScale = area.width() / effSize.width(); + if (effSize.height()*tScale > area.height()) + tScale = area.height() / effSize.height(); + const float scale = interpolate(1.0, tScale, winData->highlight); + if (scale > 1.0) { + if (scale < tScale) // don't use lanczos during transition + mask &= ~PAINT_WINDOW_LANCZOS; + + const QPoint ac = area.center(); + const QPoint wc = rect.center(); + + data.xScale *= scale; + data.yScale *= scale; + const int tx = -w->width()*data.xScale*(scale-1.0)*(0.5+(wc.x() - ac.x())/area.width()); + const int ty = -w->height()*data.yScale*(scale-1.0)*(0.5+(wc.y() - ac.y())/area.height()); + rect.translate(tx,ty); + rect.setWidth(rect.width()*scale); + rect.setHeight(rect.height()*scale); + data.xTranslate += tx; + data.yTranslate += ty; + } + } - if (!m_motionManager.areWindowsMoving()) { - mask |= PAINT_WINDOW_LANCZOS; + if (m_motionManager.areWindowsMoving()) { + mask &= ~PAINT_WINDOW_LANCZOS; } if (m_dragInProgress && m_dragWindow == w) { QPoint diff = cursorPos() - m_dragStart; @@ -350,7 +388,7 @@ void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region } effects->paintWindow(w, mask, region, data); - QRect rect = m_motionManager.transformedGeometry(w).toRect(); + if (m_showIcons) { QPoint point(rect.x() + rect.width() * 0.95, rect.y() + rect.height() * 0.95); @@ -484,7 +522,7 @@ void PresentWindowsEffect::windowInputMouseEvent(Window w, QEvent *e) } if (m_closeView->isVisible()) { const QPoint widgetPos = m_closeView->mapFromGlobal(me->pos()); - const QPointF scenePos = m_closeView->mapToScene(widgetPos); +// const QPointF scenePos = m_closeView->mapToScene(widgetPos); QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); m_closeView->windowInputMouseEvent(&event); return; @@ -1546,6 +1584,7 @@ void PresentWindowsEffect::setActive(bool active, bool closingTab) } m_activated = active; if (m_activated) { + m_closeButtonCorner = (Qt::Corner)effects->kwinOption(KWin::CloseButtonCorner).toInt(); m_decalOpacity = 0.0; m_highlightedWindow = NULL; m_windowFilter.clear(); @@ -1648,6 +1687,8 @@ void PresentWindowsEffect::setActive(bool active, bool closingTab) } } } else { + if (m_highlightedWindow) + effects->setElevatedWindow(m_highlightedWindow, false); // Fade in/out all windows EffectWindow *activeWindow = effects->activeWindow(); if (m_tabBoxEnabled) @@ -1752,32 +1793,65 @@ void PresentWindowsEffect::setHighlightedWindow(EffectWindow *w) return; m_closeView->hide(); - if (m_highlightedWindow) + if (m_highlightedWindow) { + effects->setElevatedWindow(m_highlightedWindow, false); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint + } m_highlightedWindow = w; - if (m_highlightedWindow) + if (m_highlightedWindow) { + effects->setElevatedWindow(m_highlightedWindow, true); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint + } if (m_tabBoxEnabled && m_highlightedWindow) effects->setTabBoxWindow(w); updateCloseWindow(); } +void PresentWindowsEffect::elevateCloseWindow() +{ + if (EffectWindow *cw = effects->findWindow(m_closeView->winId())) + effects->setElevatedWindow(cw, true); +} + void PresentWindowsEffect::updateCloseWindow() { if (m_doNotCloseWindows) return; + if (!m_highlightedWindow || m_highlightedWindow->isDesktop()) { + m_closeView->hide(); + return; + } if (m_closeView->isVisible()) return; - if (!m_highlightedWindow) { + + const QRectF rect(m_motionManager.targetGeometry(m_highlightedWindow)); + if (2*m_closeView->sceneRect().width() > rect.width() && 2*m_closeView->sceneRect().height() > rect.height()) { + // not for tiny windows (eg. with many windows) - they might become unselectable m_closeView->hide(); return; } - const QRectF rect = m_motionManager.targetGeometry(m_highlightedWindow); - m_closeView->setGeometry(rect.x() + rect.width() - m_closeView->sceneRect().width(), rect.y(), - m_closeView->sceneRect().width(), m_closeView->sceneRect().height()); - if (rect.contains(effects->cursorPos())) - m_closeView->delayedShow(); + QRect cvr(QPoint(0,0), m_closeView->sceneRect().size().toSize()); + switch (m_closeButtonCorner) + { + case Qt::TopLeftCorner: + default: + cvr.moveTopLeft(rect.topLeft().toPoint()); break; + case Qt::TopRightCorner: + cvr.moveTopRight(rect.topRight().toPoint()); break; + case Qt::BottomLeftCorner: + cvr.moveBottomLeft(rect.bottomLeft().toPoint()); break; + case Qt::BottomRightCorner: + cvr.moveBottomRight(rect.bottomRight().toPoint()); break; + } + m_closeView->setGeometry(cvr); + if (rect.contains(effects->cursorPos())) { + m_closeView->show(); + m_closeView->disarm(); + // to wait for the next event cycle (or more if the show takes more time) + // TODO: make the closeWindow a graphicsviewitem? why should there be an extra scene to be used in an exiting scene?? + QTimer::singleShot(50, this, SLOT(elevateCloseWindow())); + } else m_closeView->hide(); } @@ -1972,7 +2046,7 @@ void PresentWindowsEffect::globalShortcutChangedClass(const QKeySequence& seq) ************************************************/ CloseWindowView::CloseWindowView(QWidget* parent) : QGraphicsView(parent) - , m_delayedShowTimer(new QTimer(this)) + , m_armTimer(new QTimer(this)) { setWindowFlags(Qt::X11BypassWindowManagerHint); setAttribute(Qt::WA_TranslucentBackground); @@ -2013,15 +2087,15 @@ CloseWindowView::CloseWindowView(QWidget* parent) scene->setSceneRect(QRectF(QPointF(0, 0), QSizeF(width, height))); setScene(scene); - // setup the timer - m_delayedShowTimer->setSingleShot(true); - m_delayedShowTimer->setInterval(500); - connect(m_delayedShowTimer, SIGNAL(timeout()), SLOT(show())); + // setup the timer - attempt to prevent accidental clicks + m_armTimer->setSingleShot(true); + m_armTimer->setInterval(350); // 50ms until the window is elevated (seen!) and 300ms more to be "realized" by the user. + connect(m_armTimer, SIGNAL(timeout()), SLOT(arm())); } void CloseWindowView::windowInputMouseEvent(QMouseEvent* e) { - if (m_delayedShowTimer->isActive()) + if (!isEnabled()) return; if (e->type() == QEvent::MouseMove) { mouseMoveEvent(e); @@ -2041,17 +2115,15 @@ void CloseWindowView::drawBackground(QPainter* painter, const QRectF& rect) m_frame->paintFrame(painter); } -void CloseWindowView::hide() +void CloseWindowView::arm() { - m_delayedShowTimer->stop(); - QWidget::hide(); + setEnabled(true); } -void CloseWindowView::delayedShow() +void CloseWindowView::disarm() { - if (isVisible() || m_delayedShowTimer->isActive()) - return; - m_delayedShowTimer->start(); + setEnabled(false); + m_armTimer->start(); } diff --git a/kwin/effects/presentwindows/presentwindows.h b/kwin/effects/presentwindows/presentwindows.h index 58c5266..e0c982e 100644 --- a/kwin/effects/presentwindows/presentwindows.h +++ b/kwin/effects/presentwindows/presentwindows.h @@ -46,8 +46,9 @@ public: void windowInputMouseEvent(QMouseEvent* e); virtual void drawBackground(QPainter* painter, const QRectF& rect); - void delayedShow(); - void hide(); + void disarm(); +public slots: + void arm(); Q_SIGNALS: void close(); @@ -55,7 +56,7 @@ Q_SIGNALS: private: Plasma::PushButton* m_closeButton; Plasma::FrameSvg* m_frame; - QTimer* m_delayedShowTimer; + QTimer* m_armTimer; }; /** @@ -160,6 +161,7 @@ public slots: private slots: void closeWindow(); + void elevateCloseWindow(); protected: // Window rearranging @@ -265,6 +267,7 @@ private: CloseWindowView* m_closeView; EffectWindow* m_closeWindow; + Qt::Corner m_closeButtonCorner; // drag to close QPoint m_dragStart; diff --git a/kwin/effects/screenlocker/CMakeLists.txt b/kwin/effects/screenlocker/CMakeLists.txt new file mode 100644 index 0000000..21d9284 --- /dev/null +++ b/kwin/effects/screenlocker/CMakeLists.txt @@ -0,0 +1,13 @@ +####################################### +# Effect + +# Source files +set( kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} + screenlocker/screenlocker.cpp + ) + +# .desktop files +install( FILES + screenlocker/screenlocker.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/kwin ) + diff --git a/kwin/effects/screenlocker/qml/main.qml b/kwin/effects/screenlocker/qml/main.qml new file mode 100644 index 0000000..36be712 --- /dev/null +++ b/kwin/effects/screenlocker/qml/main.qml @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright 2011 Artur Duque de Souza * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + + +Rectangle { + id: lockScreen + width: 800 + height: 600 + color: Qt.rgba(0, 0, 0, 0.8) + + signal unlockRequested() + + MouseArea { + anchors.fill: parent + onPressed: mouse.accepted = true + } + + PlasmaCore.Theme { + id: theme + } + + Behavior on opacity { + NumberAnimation {duration: 250} + } + + Item { + id: lockArea + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: 120 + } + + Item { + id: unlockArea + anchors { + top: parent.top + bottom: lockArea.top + left: parent.left + right: parent.right + } + + + Text { + id: unlockText + text: i18n("Drag here to unlock") + color: "white" + anchors.centerIn: parent + font.pixelSize: 36 + font.family: theme.font.family + font.bold: theme.font.bold + font.capitalization: theme.font.capitalization + font.italic: theme.font.italic + font.weight: theme.font.weight + font.underline: theme.font.underline + font.wordSpacing: theme.font.wordSpacing + opacity: 0 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + } + + } + + + Image { + id: lockerImage + width: 64 + height: 64 + source: "unlock-normal.png" + state: "default" + + anchors { + bottom: lockScreen.bottom + topMargin: 10 + horizontalCenter: lockScreen.horizontalCenter + } + + MouseArea { + anchors.fill: parent + drag.target: parent + onPressed: { + lockerImage.state = "drag" + } + + onReleased: { + var pos = (lockerImage.x > unlockArea.x && lockerImage.y > unlockArea.y); + var size = (lockerImage.x < unlockArea.width && lockerImage.y < unlockArea.height); + + if (pos && size) { + lockScreen.unlockRequested() + } + + lockerImage.state = "default" + } + } + + states: [ + State { + name: "drag" + PropertyChanges { + target: lockerImage + anchors.bottom: undefined + anchors.horizontalCenter: undefined + source: "unlock-pressed.png" + } + PropertyChanges { + target: unlockText + opacity: 0.6 + } + }, + State { + name: "default" + PropertyChanges { + target: lockerImage + anchors.bottom: lockScreen.bottom + anchors.horizontalCenter: lockScreen.horizontalCenter + source: "unlock-normal.png" + } + PropertyChanges { + target: unlockText + opacity: 0 + } + } + ] + } +} + diff --git a/kwin/effects/screenlocker/qml/unlock-normal.png b/kwin/effects/screenlocker/qml/unlock-normal.png new file mode 100644 index 0000000..e67472e Binary files /dev/null and b/kwin/effects/screenlocker/qml/unlock-normal.png differ diff --git a/kwin/effects/screenlocker/qml/unlock-pressed.png b/kwin/effects/screenlocker/qml/unlock-pressed.png new file mode 100644 index 0000000..cd8e0cf Binary files /dev/null and b/kwin/effects/screenlocker/qml/unlock-pressed.png differ diff --git a/kwin/effects/screenlocker/screenlocker.cpp b/kwin/effects/screenlocker/screenlocker.cpp new file mode 100644 index 0000000..df20014 --- /dev/null +++ b/kwin/effects/screenlocker/screenlocker.cpp @@ -0,0 +1,234 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 "screenlocker.h" +#include +#include +#include +#include +#include +#include +#ifdef KWIN_HAVE_OPENGL +#include +#endif +#ifdef KWIN_HAVE_XRENDER_COMPOSITING +#include +#endif +#include + +namespace KWin +{ + +KWIN_EFFECT(screenlocker, ScreenLockerEffect) + +class EffectDeclarativeView : public QDeclarativeView +{ +public: + void windowInputMouseEvent(QEvent *e) + { + QMouseEvent *ev = static_cast(e); + + if (e->type() == QEvent::MouseMove) { + mouseMoveEvent(ev); + } else if (e->type() == QEvent::MouseButtonPress) { + mousePressEvent(ev); + } else if (e->type() == QEvent::MouseButtonDblClick) { + mouseDoubleClickEvent(ev); + } else if (e->type() == QEvent::MouseButtonRelease) { + mouseReleaseEvent(ev); + } + } +}; + +ScreenLockerEffect::ScreenLockerEffect() + : Effect() + , m_locked(false) + , m_inputWindow(0) +{ + connect(effects, SIGNAL(requestScreenLock()), SLOT(slotRequestLock())); + m_declarativeView = new EffectDeclarativeView; + foreach(const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { + m_declarativeView->engine()->addImportPath(importPath); + } + KDeclarative kdeclarative; + kdeclarative.setDeclarativeEngine(m_declarativeView->engine()); + kdeclarative.initialize(); + //binds things like kconfig and icons + kdeclarative.setupBindings(); + m_declarativeView->setSource(QUrl(KStandardDirs::locate("data", "kwin/lockscreen/main.qml"))); + m_declarativeView->setResizeMode(QDeclarativeView::SizeRootObjectToView); + m_declarativeView->setWindowFlags(Qt::X11BypassWindowManagerHint); + m_declarativeView->setAttribute(Qt::WA_TranslucentBackground); + m_declarativeView->setFrameShape(QFrame::NoFrame); + + connect(m_declarativeView->rootObject(), SIGNAL(unlockRequested()), this, SLOT(slotRequestUnlock())); + //m_declarativeView->hide(); +} + +ScreenLockerEffect::~ScreenLockerEffect() +{ + delete m_declarativeView; +} + +void ScreenLockerEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) +{ + effects->paintScreen(mask, region, data); + if (m_locked) { + // TODO: do our screen locking + } +} + +void ScreenLockerEffect::postPaintScreen() +{ + if (m_locked) { + // paint screen black + // TODO: add cross fading + if (effects->compositingType() == OpenGLCompositing) { + paintGL(); + } else if (effects->compositingType() == XRenderCompositing) { + paintXrender(); + } + EffectWindow *w = effects->findWindow(m_declarativeView->winId()); + if (w) { + WindowPaintData d(w); + effects->drawWindow(w, PAINT_WINDOW_OPAQUE, infiniteRegion(), d); + } + } +} + +void ScreenLockerEffect::paintGL() +{ +#ifdef KWIN_HAVE_OPENGL + GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); + vbo->reset(); + vbo->setUseColor(true); + if (ShaderManager::instance()->isValid()) { + ShaderManager::instance()->pushShader(ShaderManager::ColorShader); + } + vbo->setColor(Qt::black); + QVector verts; + const QRect r(0, 0, displayWidth(), displayHeight()); + verts.reserve(12); + verts << r.x() + r.width() << r.y(); + verts << r.x() << r.y(); + verts << r.x() << r.y() + r.height(); + verts << r.x() << r.y() + r.height(); + verts << r.x() + r.width() << r.y() + r.height(); + verts << r.x() + r.width() << r.y(); + vbo->setData(6, 2, verts.data(), NULL); + vbo->render(GL_TRIANGLES); + if (ShaderManager::instance()->isValid()) { + ShaderManager::instance()->popShader(); + } +#endif +} + +void ScreenLockerEffect::paintXrender() +{ +#ifdef KWIN_HAVE_XRENDER_COMPOSITING + XRenderColor col; + col.alpha = 1; + col.red = 0; + col.green = 0; + col.blue = 0; + XRenderFillRectangle(display(), PictOpOver, effects->xrenderBufferPicture(), + &col, 0, 0, displayWidth(), displayHeight()); +#endif +} + +void ScreenLockerEffect::slotRequestLock() +{ + if (!effects->isScreenLockerReferenced()) { + m_declarativeView->setGeometry(effects->clientArea(FullScreenArea, effects->activeScreen(), effects->currentDesktop())); + m_declarativeView->show(); + // create input window and grab mouse + Window w = effects->createFullScreenInputWindow(this, Qt::PointingHandCursor); + if (!w) { + return; + } + if (!effects->grabKeyboard(this)) { + effects->destroyInputWindow(w); + return; + } + effects->refScreenLocker(this); + effects->setActiveFullScreenEffect(this); + m_locked = true; + m_inputWindow = w; + + effects->addRepaintFull(); + } +} + +void ScreenLockerEffect::grabbedKeyboardEvent(QKeyEvent *e) +{ + if (!m_locked) { + return; + } + // TODO: implement a proper unlock behavior + if (e->key() == Qt::Key_Space) { + // magic key for testing + doUnlock(); + } +} + +void ScreenLockerEffect::slotRequestUnlock() +{ + doUnlock(); +} + +void ScreenLockerEffect::windowInputMouseEvent(Window w, QEvent *e) +{ + Q_UNUSED(w) + Q_UNUSED(e) + if (!m_locked) { + return; + } + + QMouseEvent* me = static_cast< QMouseEvent* >(e); + QMouseEvent event(me->type(), me->pos(), me->pos(), me->button(), me->buttons(), me->modifiers()); + m_declarativeView->windowInputMouseEvent(&event); + // TODO: implement proper behavior by triggering the unlock screen +} + +void ScreenLockerEffect::doUnlock() +{ + m_locked = false; + effects->destroyInputWindow(m_inputWindow); + m_inputWindow = 0; + effects->setActiveFullScreenEffect(NULL); + effects->ungrabKeyboard(); + effects->unrefScreenLocker(this); + m_declarativeView->hide(); + effects->addRepaintFull(); +} + +bool ScreenLockerEffect::isActive() const +{ + return m_locked; +} + +bool ScreenLockerEffect::provides(Effect::Feature feature) +{ + if (feature == ScreenLocking) { + return true; + } + return false; +} + +} //namespace diff --git a/kwin/effects/screenlocker/screenlocker.desktop b/kwin/effects/screenlocker/screenlocker.desktop new file mode 100644 index 0000000..3631f35 --- /dev/null +++ b/kwin/effects/screenlocker/screenlocker.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Screen Locker +Icon=preferences-system-windows-effect-screenlocker +Comment=Internal Helper Effect to perform screen locking + +Type=Service +X-KDE-ServiceTypes=KWin/Effect +X-KDE-PluginInfo-Author=Martin Gräßlin +X-KDE-PluginInfo-Email=mgraesslin@kde.org +X-KDE-PluginInfo-Name=kwin4_effect_screenlocker +X-KDE-PluginInfo-Version=0.1.0 +X-KDE-PluginInfo-Category=Window Management +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-Library=kwin4_effect_builtins +X-KDE-Ordering=99 \ No newline at end of file diff --git a/kwin/effects/screenlocker/screenlocker.h b/kwin/effects/screenlocker/screenlocker.h new file mode 100644 index 0000000..7a21a80 --- /dev/null +++ b/kwin/effects/screenlocker/screenlocker.h @@ -0,0 +1,57 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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_SCREENLOCKER_EFFECT_H +#define KWIN_SCREENLOCKER_EFFECT_H +#include + + +namespace KWin +{ + +class EffectDeclarativeView; + +class ScreenLockerEffect : public Effect +{ + Q_OBJECT +public: + ScreenLockerEffect(); + virtual ~ScreenLockerEffect(); + virtual void paintScreen(int mask, QRegion region, ScreenPaintData &data); + virtual void postPaintScreen(); + virtual bool provides(Feature feature); + virtual bool isActive() const; + + virtual void windowInputMouseEvent(Window w, QEvent *e); + virtual void grabbedKeyboardEvent(QKeyEvent *e); + +public Q_SLOTS: + void slotRequestLock(); + void slotRequestUnlock(); + +private: + void doUnlock(); + void paintGL(); + void paintXrender(); + bool m_locked; + Window m_inputWindow; + EffectDeclarativeView *m_declarativeView; +}; +} // namespace +#endif diff --git a/kwin/libkdecorations/kdecorationfactory.cpp b/kwin/libkdecorations/kdecorationfactory.cpp index 05b33cf..142add7 100644 --- a/kwin/libkdecorations/kdecorationfactory.cpp +++ b/kwin/libkdecorations/kdecorationfactory.cpp @@ -28,12 +28,21 @@ DEALINGS IN THE SOFTWARE. #include "kdecorationbridge.h" -KDecorationFactory::KDecorationFactory() +class KDecorationFactoryPrivate { +public: + KDecorationFactoryPrivate() { + closeButtonCorner = (Qt::Corner)0; + } + Qt::Corner closeButtonCorner; +}; + +KDecorationFactory::KDecorationFactory() : d(new KDecorationFactoryPrivate) { } KDecorationFactory::~KDecorationFactory() { + delete d; assert(_decorations.count() == 0); } @@ -56,6 +65,18 @@ bool KDecorationFactory::exists(const KDecoration* deco) const return _decorations.contains(const_cast< KDecoration* >(deco)); } +Qt::Corner KDecorationFactory::closeButtonCorner() +{ + if (d->closeButtonCorner) + return d->closeButtonCorner; + return options()->titleButtonsLeft().contains('X') ? Qt::TopLeftCorner : Qt::TopRightCorner; +} + +void KDecorationFactory::setCloseButtonCorner(Qt::Corner cnr) +{ + d->closeButtonCorner = cnr; +} + void KDecorationFactory::addDecoration(KDecoration* deco) { _decorations.append(deco); diff --git a/kwin/libkdecorations/kdecorationfactory.h b/kwin/libkdecorations/kdecorationfactory.h index 978d4b2..44a5b7d 100644 --- a/kwin/libkdecorations/kdecorationfactory.h +++ b/kwin/libkdecorations/kdecorationfactory.h @@ -88,6 +88,21 @@ public: * after such actions. */ bool exists(const KDecoration* deco) const; + + /** + * Set & get the position of the close button - most decorations don't have to call this ever. + * + * By default, the legacy position indicated by the options (top left or top right) will be + * returned. + * Only if you need to provide a bottom corner or your decoration does not respect those + * settings you will have to specify the exact corner (eg. used by the "present windows" + * closer) + * + * @since 4.8 + */ + Qt::Corner closeButtonCorner(); + void setCloseButtonCorner(Qt::Corner cnr); + /** * @internal */ diff --git a/kwin/libkwineffects/kwineffects.h b/kwin/libkwineffects/kwineffects.h index 87a676d..6f32068 100644 --- a/kwin/libkwineffects/kwineffects.h +++ b/kwin/libkwineffects/kwineffects.h @@ -166,7 +166,7 @@ X-KDE-Library=kwin4_effect_cooleffect #define KWIN_EFFECT_API_MAKE_VERSION( major, minor ) (( major ) << 8 | ( minor )) #define KWIN_EFFECT_API_VERSION_MAJOR 0 -#define KWIN_EFFECT_API_VERSION_MINOR 180 +#define KWIN_EFFECT_API_VERSION_MINOR 181 #define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \ KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR ) @@ -331,7 +331,8 @@ public: enum Feature { Nothing = 0, Resize, GeometryTip, - Outline + Outline, + ScreenLocking }; /** @@ -580,6 +581,7 @@ public: virtual void paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity) = 0; virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) = 0; virtual void buildQuads(EffectWindow* w, WindowQuadList& quadList) = 0; + virtual QVariant kwinOption(KWinOption kwopt) = 0; // Functions for handling input - e.g. when an Expose-like effect is shown, an input window // covering the whole screen is created and all mouse events will be intercepted by it. // The effect's windowInputMouseEvent() will get called with such events. @@ -780,6 +782,42 @@ public: virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize = true, const QPoint& position = QPoint(-1, -1), Qt::Alignment alignment = Qt::AlignCenter) const = 0; + + /** + * Allows effects to indicate that they are responsible for handling screen locking. + * Only one effect at a time should reference the Screen Locker. It is assumed that + * all effects want to collaborate and will check @link isScreenLockerReferenced before + * trying to reference the screen locker. + * When the effect gets into a state that it wants to indicate that the screen got unlocked + * it should use @link unrefScreenLocker to remove the reference on the screen locker and + * to indicate that the screen got unlocked. + * + * An Effect may only call this method in a slot connected on the @link requestScreenLock signal. + * Calling this method outside the handling of that signal will not do anything. + * + * @param lockEffect The Effect which implements the lock. + * @since 4.8 + * @see unrefScreenLocker + * @see isScreenLockerReferenced + **/ + virtual void refScreenLocker(Effect *lockEffect) = 0; + /** + * Method to unlock the screen for an Effect which handles Screen Locking through + * @link refScreenLocker. + * If the Effect had not referenced the Screen Locker this method will not unlock the screen. + * @param lockEffect The Effect which implements the lock. + * @since 4.8 + * @see refScreenLocker + **/ + virtual void unrefScreenLocker(Effect *lockEffect) = 0; + /** + * @returns Whether an Effect referenced the Screen Lock. + * @since 4.8 + * @see refScreenLocker + * @see unrefScreenLocker + **/ + virtual bool isScreenLockerReferenced() const = 0; + /** * Sends message over DCOP to reload given effect. * @param effectname effect's name without "kwin4_effect_" prefix. @@ -1017,6 +1055,14 @@ Q_SIGNALS: * @since 4.7 **/ void hideOutline(); + /** + * Signal emitted when the screen is about to be locked. An effect implementing a screen lock + * may connect to this signal and call @link refScreenLocker in the slot handling the signal. + * @since 4.8 + * @see refScreenLocker + * @see isScreenLockerReferenced + **/ + void requestScreenLock(); protected: QVector< EffectPair > loaded_effects; diff --git a/kwin/libkwineffects/kwinglobals.h b/kwin/libkwineffects/kwinglobals.h index 2a4ea71..b33d3df 100644 --- a/kwin/libkwineffects/kwinglobals.h +++ b/kwin/libkwineffects/kwinglobals.h @@ -105,6 +105,10 @@ enum TabBoxMode { TabBoxWindowsAlternativeMode // Secondary window switching mode }; +enum KWinOption { + CloseButtonCorner +}; + inline KWIN_EXPORT Display* display() { diff --git a/kwin/screenlocker/config-xautolock.h.cmake b/kwin/screenlocker/config-xautolock.h.cmake new file mode 100644 index 0000000..eadb0a6 --- /dev/null +++ b/kwin/screenlocker/config-xautolock.h.cmake @@ -0,0 +1,2 @@ +/* Define if you have the XScreenSaver extension */ +#cmakedefine HAVE_XSCREENSAVER 1 diff --git a/kwin/screenlocker/dbus/org.freedesktop.ScreenSaver.xml b/kwin/screenlocker/dbus/org.freedesktop.ScreenSaver.xml new file mode 100644 index 0000000..5efd943 --- /dev/null +++ b/kwin/screenlocker/dbus/org.freedesktop.ScreenSaver.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kwin/screenlocker/dbus/org.kde.screensaver.xml b/kwin/screenlocker/dbus/org.kde.screensaver.xml new file mode 100644 index 0000000..e700b88 --- /dev/null +++ b/kwin/screenlocker/dbus/org.kde.screensaver.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/kwin/screenlocker/kcfg/kscreensaversettings.kcfg b/kwin/screenlocker/kcfg/kscreensaversettings.kcfg new file mode 100644 index 0000000..c8f76f3 --- /dev/null +++ b/kwin/screenlocker/kcfg/kscreensaversettings.kcfg @@ -0,0 +1,92 @@ + + + kglobalsettings.h + + + + false + + Enables the screen saver. + + + 300 + + Sets the seconds after which the screen saver is started. + + + true + + Usually the screen saver is suspended when display power saving kicks in, + as nothing can be seen on the screen anyway, obviously. However, some screen savers + actually perform useful computations, so it is not desirable to suspend them. + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + false + + + + + 5000 + + + + + false + + + + + 600 + + + + + 19 + + + + + + + + + + + + + + + + + + + + false + + + + + diff --git a/kwin/screenlocker/kcfg/kscreensaversettings.kcfgc b/kwin/screenlocker/kcfg/kscreensaversettings.kcfgc new file mode 100644 index 0000000..af9133d --- /dev/null +++ b/kwin/screenlocker/kcfg/kscreensaversettings.kcfgc @@ -0,0 +1,4 @@ +File=kscreensaversettings.kcfg +ClassName=KScreenSaverSettings +Singleton=true +Mutators=true diff --git a/kwin/screenlocker/lock/CMakeLists.txt b/kwin/screenlocker/lock/CMakeLists.txt new file mode 100644 index 0000000..76319be --- /dev/null +++ b/kwin/screenlocker/lock/CMakeLists.txt @@ -0,0 +1,62 @@ +include_directories( ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/screenlocker ${KDEBASE_WORKSPACE_SOURCE_DIR}/kcheckpass ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kdm ) + +########### next target ############### + +check_library_exists(Xxf86misc XF86MiscSetGrabKeysState "" HAVE_XF86MISCSETGRABKEYSSTATE) +if(WITH_OpenGL) +check_library_exists(GL glXChooseVisual "" HAVE_GLXCHOOSEVISUAL) +endif(WITH_OpenGL) + +configure_file(config-krunner-lock.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-krunner-lock.h) + +set(kscreenlocker_SRCS + lockprocess.cc + lockdlg.cc + autologout.cc + main.cc ) + +set(plasmaapp_dbusXML ../../../plasma/screensaver/shell/org.kde.plasma-overlay.App.xml) +qt4_add_dbus_interface(kscreenlocker_SRCS ${plasmaapp_dbusXML} plasmaapp_interface) + +set(lockprocess_dbusXML org.kde.screenlocker.LockProcess.xml) +qt4_generate_dbus_interface(lockprocess.h ${lockprocess_dbusXML} OPTIONS -m) +qt4_add_dbus_adaptor(kscreenlocker_SRCS ${CMAKE_CURRENT_BINARY_DIR}/${lockprocess_dbusXML} lockprocess.h LockProcess) + +set(ksmserver_xml ${KDEBASE_WORKSPACE_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml) +QT4_ADD_DBUS_INTERFACE(kscreenlocker_SRCS ${ksmserver_xml} ksmserver_interface) +set(kscreensaver_xml ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/screenlocker/dbus/org.kde.screensaver.xml) +QT4_ADD_DBUS_INTERFACE(kscreenlocker_SRCS ${kscreensaver_xml} kscreensaver_interface) + + + +kde4_add_kcfg_files(kscreenlocker_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../kcfg/kscreensaversettings.kcfgc) + + + +kde4_add_executable(kscreenlocker ${kscreenlocker_SRCS}) + +target_link_libraries(kscreenlocker kephal ${KDE4_KDEUI_LIBS} ${X11_LIBRARIES}) + +find_library(DL_LIBRARY dl) +if (DL_LIBRARY) + target_link_libraries(kscreenlocker ${DL_LIBRARY}) +endif(DL_LIBRARY) + +if (HAVE_XF86MISC) + target_link_libraries(kscreenlocker ${X11_Xxf86misc_LIB}) +endif (HAVE_XF86MISC) + +if(HAVE_GLXCHOOSEVISUAL) + target_link_libraries(kscreenlocker ${OPENGL_gl_LIBRARY}) +endif(HAVE_GLXCHOOSEVISUAL) + +install(TARGETS kscreenlocker DESTINATION ${LIBEXEC_INSTALL_DIR}) + +install_pam_service(kscreensaver) + + +########### install files ############### + + +install( FILES kscreenlocker.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kscreenlocker ) + diff --git a/kwin/screenlocker/lock/autologout.cc b/kwin/screenlocker/lock/autologout.cc new file mode 100644 index 0000000..c86e29a --- /dev/null +++ b/kwin/screenlocker/lock/autologout.cc @@ -0,0 +1,118 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 2004 Chris Howells + +#include "lockprocess.h" +#include "autologout.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COUNTDOWN 30 + +AutoLogout::AutoLogout(LockProcess *parent) : QDialog(parent, Qt::X11BypassWindowManagerHint) +{ + QLabel *pixLabel = new QLabel( this ); + pixLabel->setObjectName( QLatin1String( "pixlabel" ) ); + pixLabel->setPixmap(DesktopIcon(QLatin1String( "application-exit" ))); + + QLabel *greetLabel = new QLabel(i18n("Automatic Log Out"), this); + QLabel *infoLabel = new QLabel(i18n("To prevent being logged out, resume using this session by moving the mouse or pressing a key."), this); + + mStatusLabel = new QLabel(QLatin1String( " " ), this); + mStatusLabel->setAlignment(Qt::AlignCenter); + + QLabel *mProgressLabel = new QLabel(i18n("Time Remaining:"), this); + mProgressRemaining = new QProgressBar(this); + mProgressRemaining->setTextVisible(false); + + frameLayout = new QGridLayout(this); + frameLayout->setSpacing(KDialog::spacingHint()); + frameLayout->setMargin(KDialog::marginHint() * 2); + frameLayout->addWidget(pixLabel, 0, 0, 3, 1, Qt::AlignCenter | Qt::AlignTop); + frameLayout->addWidget(greetLabel, 0, 1); + frameLayout->addWidget(mStatusLabel, 1, 1); + frameLayout->addWidget(infoLabel, 2, 1); + frameLayout->addWidget(mProgressLabel, 3, 1); + frameLayout->addWidget(mProgressRemaining, 4, 1); + + // get the time remaining in seconds for the status label + mRemaining = COUNTDOWN * 25; + + mProgressRemaining->setMaximum(COUNTDOWN * 25); + + updateInfo(mRemaining); + + mCountdownTimerId = startTimer(1000/25); + + connect(qApp, SIGNAL(activity()), SLOT(slotActivity())); +} + +AutoLogout::~AutoLogout() +{ + hide(); +} + +void AutoLogout::updateInfo(int timeout) +{ + mStatusLabel->setText(i18np("You will be automatically logged out in 1 second", + "You will be automatically logged out in %1 seconds", + timeout / 25) ); + mProgressRemaining->setValue(timeout); +} + +void AutoLogout::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == mCountdownTimerId) + { + updateInfo(mRemaining); + --mRemaining; + if (mRemaining < 0) + { + killTimer(mCountdownTimerId); + logout(); + } + } +} + +void AutoLogout::slotActivity() +{ + if (mRemaining >= 0) + accept(); +} + +void AutoLogout::logout() +{ + QAbstractEventDispatcher::instance()->unregisterTimers(this); + org::kde::KSMServerInterface ksmserver(QLatin1String( "org.kde.ksmserver" ), QLatin1String( "/KSMServer" ), QDBusConnection::sessionBus()); + ksmserver.logout( 0, 0, 0 ); +} + +void AutoLogout::setVisible(bool visible) +{ + QDialog::setVisible(visible); + + if (visible) + QApplication::flush(); +} + +#include "autologout.moc" diff --git a/kwin/screenlocker/lock/autologout.h b/kwin/screenlocker/lock/autologout.h new file mode 100644 index 0000000..0c44405 --- /dev/null +++ b/kwin/screenlocker/lock/autologout.h @@ -0,0 +1,46 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Oswald Buddenhagen +// Copyright 2004 Chris Howells + +#ifndef AUTOLOGOUT_H +#define AUTOLOGOUT_H + +#include + +class LockProcess; +class QGridLayout; +class QLabel; +class QDialog; +class QProgressBar; + +class AutoLogout : public QDialog +{ + Q_OBJECT + +public: + AutoLogout(LockProcess *parent); + ~AutoLogout(); + virtual void setVisible(bool visible); + +protected: + virtual void timerEvent(QTimerEvent *); + +private Q_SLOTS: + void slotActivity(); + +private: + void updateInfo(int); + QGridLayout *frameLayout; + QLabel *mStatusLabel; + int mCountdownTimerId; + int mRemaining; + QTimer countDownTimer; + QProgressBar *mProgressRemaining; + void logout(); +}; + +#endif // AUTOLOGOUT_H diff --git a/kwin/screenlocker/lock/config-krunner-lock.h.cmake b/kwin/screenlocker/lock/config-krunner-lock.h.cmake new file mode 100644 index 0000000..7bfdfd6 --- /dev/null +++ b/kwin/screenlocker/lock/config-krunner-lock.h.cmake @@ -0,0 +1,2 @@ +#cmakedefine HAVE_GLXCHOOSEVISUAL 1 +#cmakedefine HAVE_XF86MISCSETGRABKEYSSTATE 1 diff --git a/kwin/screenlocker/lock/kscreenlocker.notifyrc b/kwin/screenlocker/lock/kscreenlocker.notifyrc new file mode 100644 index 0000000..9a9bce8 --- /dev/null +++ b/kwin/screenlocker/lock/kscreenlocker.notifyrc @@ -0,0 +1,740 @@ +[Global] +IconName=system-lock-screen +Comment=Screen Saver +Comment[ar]=حافظة الشاشة +Comment[ast]=Curiapantalles +Comment[bg]=Екранен предпазител +Comment[bs]=Čuvar ekrana +Comment[ca]=Estalvi de pantalla +Comment[ca@valencia]=Estalvi de pantalla +Comment[cs]=Šetřič obrazovky +Comment[da]=Pauseskærm +Comment[de]=Bildschirmschoner +Comment[el]=Προφύλαξη οθόνης +Comment[en_GB]=Screen Saver +Comment[es]=Salvapantallas +Comment[et]=Ekraanisäästja +Comment[eu]=Pantaila babeslea +Comment[fi]=Näytönsäästäjä +Comment[fr]=Écran de veille +Comment[ga]=Spárálaí Scáileáin +Comment[gu]=સ્ક્રીનક્રિન સેવર +Comment[he]=שומר מסך +Comment[hi]=स्क्रीन सेवर +Comment[hr]=Čuvar zaslona +Comment[hu]=Képernyővédő +Comment[ia]=Salvator de schermo +Comment[id]=Penyimpan Layar +Comment[is]=Skjáhvíla +Comment[it]=Salvaschermo +Comment[ja]=スクリーンセーバー +Comment[kk]=Экран сақтағышы +Comment[km]=ធាតុ​រក្សា​អេក្រង់​ +Comment[kn]=ತೆರೆ ರಕ್ಷಕ +Comment[ko]=화면 보호기 +Comment[lt]=Ekrano užsklanda +Comment[lv]=Ekrānsaudzētājs +Comment[nb]=Pauseskjerm +Comment[nds]=Pausschirm +Comment[nl]=Schermbeveiliging +Comment[nn]=Pause­skjerm +Comment[pa]=ਸਕਰੀਨ ਸੇਵਰ +Comment[pl]=Wygaszacz ekranu +Comment[pt]=Protector de Ecrã +Comment[pt_BR]=Protetor de tela +Comment[ro]=Protecție de ecran +Comment[ru]=Хранитель экрана +Comment[si]=තිර සුරැකුම +Comment[sk]=Šetrič obrazovky +Comment[sl]=Ohranjevalnik zaslona +Comment[sr]=Чувар екрана +Comment[sr@ijekavian]=Чувар екрана +Comment[sr@ijekavianlatin]=Čuvar ekrana +Comment[sr@latin]=Čuvar ekrana +Comment[sv]=Skärmsläckare +Comment[tg]=Пардаи экран +Comment[th]=โปรแกรมรักษาจอภาพ +Comment[tr]=Ekran Koruyucu +Comment[ug]=ئېكران قوغدىغۇچ +Comment[uk]=Зберігач екрана +Comment[wa]=Sipårgneu di waitroûle +Comment[x-test]=xxScreen Saverxx +Comment[zh_CN]=屏幕保护程序 +Comment[zh_TW]=螢幕保護程式 + +[Event/savingstarted] +Name=Screen saver started +Name[ar]=بدأت حافظة الشاشة +Name[ast]=Curiapantalles aniciáu +Name[bg]=Зареден е екранен предпазител +Name[bs]=Čuvar ekrana pokrenut +Name[ca]=S'ha iniciat l'estalvi de pantalla +Name[ca@valencia]=S'ha iniciat l'estalvi de pantalla +Name[cs]=Šetřič obrazovky spuštěn +Name[csb]=Zrëszony wëgaszôcz ekranu +Name[da]=Pauseskærm startet +Name[de]=Der Bildschirmschoner wurde gestartet. +Name[el]=Η προφύλαξη οθόνης ξεκίνησε +Name[en_GB]=Screen saver started +Name[eo]=Ekrankurtenon ŝaltis +Name[es]=Salvapantallas iniciado +Name[et]=Ekraanisäästja alustas tööd +Name[eu]=Pantaila babeslea abiarazita +Name[fi]=Näytönsäästäjä käynnistyi +Name[fr]=Écran de veille démarré +Name[fy]=Skermbefeiliging úteinsetten +Name[ga]=Tosaíodh an spárálaí scáileáin +Name[gl]=Iniciouse o protector de pantalla +Name[gu]=સ્ક્રિન સેવર શરૂ થયું +Name[he]=שומר המסך התחיל +Name[hi]=स्क्रीन सेवर चालू +Name[hr]=Zaštita zaslona pokrenuta +Name[hu]=A képernyővédő elindult +Name[ia]=Salvator de schermo startate +Name[id]=Penyimpan layar dijalankan +Name[is]=Skjásvæfa ræst +Name[it]=Salvaschermo avviato +Name[ja]=スクリーンセーバー開始 +Name[kk]=Экран сақтағышы ісін бастады +Name[km]=បាន​ចាប់ផ្ដើម​ធាតុរក្សា​អេក្រង់ +Name[kn]=ಸ್ಕ್ರೀನ್‌ ಸೇವರ್ ಪ್ರಾರಂಭಗೊಂಡಿದೆ +Name[ko]=화면 보호기 시작됨 +Name[lt]=Ekrano užsklanda paleista +Name[lv]=Ekrāna saudzētājs palaists +Name[mk]=Чуварот на екранот е стартуван +Name[ml]=സ്ക്രീന്‍ സേവര്‍ തുടങ്ങി +Name[nb]=Pauseskjerm startet +Name[nds]=Pausschirm opropen +Name[nl]=Schermbeveiliging gestart +Name[nn]=Pauseskjermen er starta +Name[pa]=ਸਕਰੀਨ-ਸੇਵਰ ਸ਼ੁਰੂ ਹੋਏ +Name[pl]=Wygaszacz ekranu uruchomiony +Name[pt]=O protector de ecrã foi iniciado +Name[pt_BR]=O protetor de tela foi iniciado +Name[ro]=Protecție de ecran pornită +Name[ru]=Хранитель экрана запущен +Name[si]=තිර සුරැකුම ආරම්ණ කරන ලදි +Name[sk]=Šetrič obrazovky spustený +Name[sl]=Zagon ohranjevalnika zaslona +Name[sr]=Чувар екрана покренут +Name[sr@ijekavian]=Чувар екрана покренут +Name[sr@ijekavianlatin]=Čuvar ekrana pokrenut +Name[sr@latin]=Čuvar ekrana pokrenut +Name[sv]=Skärmsläckare startad +Name[tg]=Пардаи экран сар шуд +Name[th]=โปรแกรมรักษาจอภาพเริ่มทำงานแล้ว +Name[tr]=Ekran koruyucu başlatıldı +Name[ug]=ئېكران قوغدىغۇچ باشلاندى +Name[uk]=Запущено зберігач екрана +Name[wa]=Sipårgneu di waitroûle enondé +Name[x-test]=xxScreen saver startedxx +Name[zh_CN]=屏幕保护程序已启动 +Name[zh_TW]=螢幕保護程式已啟動 +Comment=The screen saver has been started +Comment[ar]=بدأت حافظة الشاشة +Comment[ast]=Entamóse'l curiapantalles +Comment[bg]=Зареден е екранен предпазител +Comment[bs]=Pokrenut je čuvar ekrana +Comment[ca]=S'ha iniciat l'estalvi de pantalla +Comment[ca@valencia]=S'ha iniciat l'estalvi de pantalla +Comment[cs]=Šetřič obrazovky byl spuštěn +Comment[csb]=Wëgaszôcz ekranu òstôł zrëszony +Comment[da]=Pauseskærmen er blevet startet +Comment[de]=Der Bildschirmschoner wurde gestartet. +Comment[el]=Η προφύλαξη οθόνης έχει ξεκινήσει +Comment[en_GB]=The screen saver has been started +Comment[eo]=la Ekrankurteno lanĉiĝas +Comment[es]=Se ha iniciado el salvapantallas +Comment[et]=Ekraanisäästja alustas tööd +Comment[eu]=Pantaila babeslea abiarazi da +Comment[fi]=Näytönsäästäjä on käynnistynyt +Comment[fr]=L'écran de veille a été démarré +Comment[fy]=De skermbefeiliging is úteinsetten +Comment[ga]=Tosaíodh an spárálaí scáileáin +Comment[gl]=Iniciouse o protector de pantalla +Comment[gu]=સ્ક્રિન સેવર શરૂ કરાયેલ છે +Comment[he]=שומר מסך הופעל +Comment[hr]=Zaštita zaslona je pokrenuta +Comment[hu]=A képernyővédő elindult +Comment[ia]=Le salvator de schermo ha essite startate +Comment[id]=Penyimpan layar telah dijalankan +Comment[is]=Skjáhvílan hefur verið ræst +Comment[it]=Il salvaschermo è stato avviato +Comment[ja]=スクリーンセーバーが開始されました +Comment[kk]=Экран сақтағышы ісін бастады +Comment[km]=ធាតុ​រក្សាអេក្រង់​ត្រូវ​បាន​ចាប់ផ្ដើម +Comment[kn]=ಸ್ಕ್ರೀನ್‌ ಸೇವರನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗಿದೆ +Comment[ko]=화면 보호기 시작됨 +Comment[lt]=Ekrano užsklanda buvo paleista +Comment[lv]=Ekrāna saudzētājs tika palaists +Comment[mk]=Чуварот на екранот беше стартуван +Comment[ml]=സ്ക്രീന്‍ സേവര്‍ തുടങ്ങിയിരിയ്ക്കുന്നു +Comment[nb]=Pauseskjermen er startet +Comment[nds]=De Pausschirm wöör opropen +Comment[nl]=De schermbeveiliging is gestart +Comment[nn]=Pauseskjermen er starta +Comment[pa]=ਸਕਰੀਨਸੇਵਰ ਸ਼ੁਰੂ ਕੀਤਾ ਜਾ ਚੁੱਕਿਆ ਹੈ +Comment[pl]=Wygaszacz ekranu został uruchomiony +Comment[pt]=O protector de ecrã foi iniciado +Comment[pt_BR]=O protetor de tela foi iniciado +Comment[ro]=Protecția de ecran a fost pornită +Comment[ru]=Хранитель экрана запущен +Comment[si]=තිර සුරැකුම ආරම්භ කර ඇත +Comment[sk]=Šetrič obrazovky bol spustený +Comment[sl]=Ohranjevalnik zaslona se je zagnal +Comment[sr]=Покренут је чувар екрана +Comment[sr@ijekavian]=Покренут је чувар екрана +Comment[sr@ijekavianlatin]=Pokrenut je čuvar ekrana +Comment[sr@latin]=Pokrenut je čuvar ekrana +Comment[sv]=Skärmsläckaren har startats +Comment[tg]=Ахлотдон холӣ карда шуд +Comment[th]=โปรแกรมรักษาจอภาพเริ่มการทำงานแล้ว +Comment[tr]=Ekran koruyucu başlatıldı +Comment[ug]=ئېكران قوغدىغۇچ باشلاندى +Comment[uk]=Запущено зберігач екрана +Comment[wa]=Li spårgneu di waitroûle a stî enondé +Comment[x-test]=xxThe screen saver has been startedxx +Comment[zh_CN]=屏幕保护程序已经启动 +Comment[zh_TW]=螢幕保護程式已被啟動 +Action=None + +[Event/locked] +Name=Screen locked +Name[ar]=قُقلت الشاشة +Name[ast]=Pantalla bloquiada +Name[bg]=Екранът е заключен +Name[bs]=Ekran zaključan +Name[ca]=S'ha bloquejat la pantalla +Name[ca@valencia]=S'ha bloquejat la pantalla +Name[cs]=Obrazovka uzamčena +Name[csb]=Zablokòwóny ekranu +Name[da]=Skærmen er låst +Name[de]=Bildschirm gesperrt +Name[el]=Οθόνη κλειδώθηκε +Name[en_GB]=Screen locked +Name[eo]=Ekranŝloso +Name[es]=Pantalla bloqueada +Name[et]=Ekraan on lukustatud +Name[eu]=Pantaila giltzatuta +Name[fi]=Näyttö lukittu +Name[fr]=Écran verrouillé +Name[fy]=Skerm beskoattele +Name[ga]=Tá an scáileán faoi ghlas +Name[gl]=A pantalla está trancada +Name[gu]=સ્ક્રિન તાળું મારેલ છે +Name[he]=מסך נעול +Name[hi]=स्क्रीन तालाबंद +Name[hr]=Zaslon zaključan +Name[hu]=A képernyő zárolt +Name[ia]=Schermo blocate +Name[id]=Layar dikunci +Name[is]=Skjár læstur +Name[it]=Schermo bloccato +Name[ja]=スクリーンロック +Name[kk]=Экран бұғатталды +Name[km]=បាន​ចាក់សោ​អេក្រង់​ +Name[kn]=ತೆರೆ ಲಾಕ್ ಆಗಿದೆ +Name[ko]=화면 잠김 +Name[lt]=Ekranas užrakintas +Name[lv]=Ekrāns slēgts +Name[mk]=Екранот е заклучен +Name[ml]=സ്ക്രീന്‍ പൂട്ടി +Name[nb]=Skjermen låst +Name[nds]=Schirm afslaten +Name[nl]=Scherm vergrendeld +Name[nn]=Skjermen er låst +Name[pa]=ਸਕਰੀਨ ਲਾਕ ਹੈ +Name[pl]=Ekran zablokowany +Name[pt]=Ecrã bloqueado +Name[pt_BR]=Tela bloqueada +Name[ro]=Ecran blocat +Name[ru]=Экран заблокирован +Name[si]=තිරය අගුලු දමන ලදි +Name[sk]=Obrazovka zamknutá +Name[sl]=Zaklep zaslona +Name[sr]=Екран закључан +Name[sr@ijekavian]=Екран закључан +Name[sr@ijekavianlatin]=Ekran zaključan +Name[sr@latin]=Ekran zaključan +Name[sv]=Skärm låst +Name[tg]=Пардаи экран қулф шуд +Name[th]=หน้าจอถูกล็อคอยู่ +Name[tr]=Ekran kilitlendi +Name[ug]=ئېكران قۇلۇپلاندى +Name[uk]=Екран заблоковано +Name[wa]=Waitroûle eclawêye +Name[x-test]=xxScreen lockedxx +Name[zh_CN]=屏幕已锁定 +Name[zh_TW]=螢幕已鎖定 +Comment=The screen has been locked +Comment[ar]=تمّ قفل الشاشة +Comment[ast]=Bloquióse la pantalla +Comment[bg]=Екранът е заключен +Comment[bs]=Ekran je upravo zaključan +Comment[ca]=S'ha bloquejat la pantalla +Comment[ca@valencia]=S'ha bloquejat la pantalla +Comment[cs]=Obrazovka byla uzamčena +Comment[csb]=Ekran òstôł zablokòwóny +Comment[da]=Skærmen er blevet låst +Comment[de]=Der Bildschirm wurde gesperrt. +Comment[el]=Η οθόνη έχει κλειδωθεί +Comment[en_GB]=The screen has been locked +Comment[eo]=La ekrano ŝlosiĝis +Comment[es]=Se ha bloqueado la pantalla +Comment[et]=Ekraan on lukustatud +Comment[eu]=Pantaila giltzatu egin da +Comment[fi]=Näyttö on lukittunut +Comment[fr]=L'écran a été verrouillé +Comment[fy]=It skerm is beskoattele +Comment[ga]=Cuireadh an scáileán faoi ghlas +Comment[gl]=A pantalla trancouse +Comment[gu]=સ્ક્રિનને તાળું મારવામાં આવ્યું છે +Comment[he]=המסך ננעל +Comment[hi]=स्क्रीन तालाबंद कर दिया गया है +Comment[hr]=Zaslon je zaključan +Comment[hu]=A képernyő zárolt +Comment[ia]=Le schermo ha essite blocate +Comment[id]=Layar telah dikunci +Comment[is]=Skjánum hefur verið læst +Comment[it]=Lo schermo è stato bloccato +Comment[ja]=スクリーンがロックされました +Comment[kk]=Экран бұғатталған +Comment[km]=អេក្រង់​ត្រូវ​បាន​ចាក់សោ +Comment[kn]=ಒಂದು ತೆರೆಯನ್ನು ಲಾಕ್‌ ಮಾಡಲಾಗಿದೆ +Comment[ko]=화면 잠김 +Comment[lt]=Ekranas buvo užrakintas +Comment[lv]=Ekrāns tika slēgts +Comment[mk]=Екранот беше заклучен +Comment[ml]=സ്ക്രീന്‍ പൂട്ടിയിരിയ്ക്കുന്നു +Comment[nb]=Skjermen er nå låst +Comment[nds]=De Schirm wöör afslaten +Comment[nl]=Het scherm is vergrendeld +Comment[nn]=Skjermen er låst +Comment[pa]=ਸਕਰੀਨ ਨੂੰ ਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ +Comment[pl]=Ekran został zablokowany +Comment[pt]=O ecrã foi bloqueado +Comment[pt_BR]=A tela foi bloqueada +Comment[ro]=Ecranul a fost blocat +Comment[ru]=Экран заблокирован +Comment[si]=තිරය අගුළු දමා ඇත +Comment[sk]=Obrazovka bola zamknutá +Comment[sl]=Zaslon je bil zaklenjen +Comment[sr]=Екран је управо закључан +Comment[sr@ijekavian]=Екран је управо закључан +Comment[sr@ijekavianlatin]=Ekran je upravo zaključan +Comment[sr@latin]=Ekran je upravo zaključan +Comment[sv]=Skärmen har låsts +Comment[tg]=Клавиша модификатора зафиксирована +Comment[th]=หน้าจอถูกล็อคอยู่ +Comment[tr]=Ekran kilitlendi +Comment[ug]=ئېكران قۇلۇپلانغان +Comment[uk]=Екран було заблоковано +Comment[wa]=Li waitroûle a stî eclawêye +Comment[x-test]=xxThe screen has been lockedxx +Comment[zh_CN]=屏幕已经被锁定 +Comment[zh_TW]=螢幕已被鎖定 +Action=None + +[Event/savingstopped] +Name=Screen saver exited +Name[ar]=خرجت حافظة الشاشة +Name[ast]=Finó'l curiapantalles +Name[bg]=Екранният предпазител е спрян +Name[bs]=Čuvar ekrana napušten +Name[ca]=S'ha sortit de l'estalvi de pantalla +Name[ca@valencia]=S'ha eixit de l'estalvi de pantalla +Name[cs]=Šetřič obrazovky ukončen +Name[csb]=Wëgaszôcz ekranu òstôł zakùńczony +Name[da]=Pauseskærm afslutter +Name[de]=Der Bildschirmschoner wurde beendet. +Name[el]=Η προφύλαξη οθόνης τερμάτισε +Name[en_GB]=Screen saver exited +Name[eo]=Ekrankurteno finiĝis +Name[es]=El salvapantallas ha terminado +Name[et]=Ekraanisäästja lõpetas töö +Name[eu]=Pantaila babeslea amaituta +Name[fi]=Näytönsäästäjä sulkeutui +Name[fr]=Écran de veille terminé +Name[fy]=Skermbefeiliging is der útgong +Name[ga]=Scoireadh ón spárálaí scáileáin +Name[gl]=O protector de pantalla saíu +Name[he]=שומר המסך הפסיק +Name[hi]=स्क्रीन सेवर बंद +Name[hr]=Zaštita zaslona završila +Name[hu]=A képernyővédő kilépett +Name[ia]=Salvator de schermo exite +Name[id]=Penyimpan layar keluar +Name[is]=Hætt í skjásvæfu +Name[it]=Salvaschermo terminato +Name[ja]=スクリーンセーバー終了 +Name[kk]=Экран қорғаушысы тоқтады +Name[km]=បាន​ចេញ​ពី​ធាតុរក្សាអេក្រង់​ +Name[kn]=ಸ್ಕ್ರೀನ್‌ ಸೇವರ್ ನಿರ್ಗಮಿಸಿದೆ +Name[ko]=화면 보호기 종료됨 +Name[lt]=Ekrano užsklanda išsijungė +Name[lv]=Ekrāna saudzētājs apturēts +Name[mk]=Чуварот на екранот излезе +Name[ml]=സ്ക്രീന്‍ സേവറില്‍ നിന്നും പുറത്തു് കടന്നിരിയ്ക്കുന്നു +Name[nb]=Pauseskjermen avsluttet +Name[nds]=Pausschirm utmaakt +Name[nl]=Schermbeveiliger geëindigd +Name[nn]=Pauseskjermen er avslutta +Name[pa]=ਸਕਰੀਨ ਸੇਵਰ ਬੰਦ +Name[pl]=Wygaszacz ekranu zakończył się +Name[pt]=O protector de ecrã terminou +Name[pt_BR]=O protetor de tela terminou +Name[ro]=Protecția de ecran a s-a terminat +Name[ru]=Хранитель экрана завершил работу +Name[si]=තිර සුරැකුම ඉවත් විය +Name[sk]=Šetrič obrazovky skončil +Name[sl]=Izhod iz ohranjevalnika zaslona +Name[sr]=Чувар екрана напуштен +Name[sr@ijekavian]=Чувар екрана напуштен +Name[sr@ijekavianlatin]=Čuvar ekrana napušten +Name[sr@latin]=Čuvar ekrana napušten +Name[sv]=Skärmsläckare avslutades +Name[tg]=Пардаи экран хомӯш шуд +Name[th]=โปรแกรมรักษาจอภาพจบการทำงานแล้ว +Name[tr]=Ekran koruyucudan çıkıldı +Name[ug]=ئېكران قوغدىغۇچ ئاخىرلاشتى +Name[uk]=Завершено роботу зберігача екрана +Name[wa]=Sipårgneu di waitroûle a cwité +Name[x-test]=xxScreen saver exitedxx +Name[zh_CN]=屏幕保护程序已退出 +Name[zh_TW]=螢幕保護程式已離開 +Comment=The screen saver has finished +Comment[ar]=حافظة الشاشة انتهت +Comment[ast]=Finó'l curiapantalles +Comment[bg]=Екранният предпазител е спрян +Comment[bs]=Čuvar ekrana se upravo okončao +Comment[ca]=L'estalvi de pantalla ha finalitzat +Comment[ca@valencia]=L'estalvi de pantalla ha finalitzat +Comment[cs]=Šetřič obrazovky byl ukončen +Comment[csb]=Wëgaszôcz ekranu òstôł zakùńczony +Comment[da]=Pauseskærmen er afsluttet +Comment[de]=Der Bildschirmschoner wurde beendet. +Comment[el]=Η προφύλαξη οθόνης έχει τελειώσει +Comment[en_GB]=The screen saver has finished +Comment[eo]=La ekrankurteno finiĝis +Comment[es]=El salvapantallas ha terminado +Comment[et]=Ekraanisäästja lõpetas töö +Comment[eu]=Pantaila babeslea amaitu da +Comment[fi]=Näytönsäästäjä on päättynyt +Comment[fr]=L'écran de veille a terminé +Comment[fy]=De skermbefeiliging is foltôge +Comment[ga]=Tá an spárálaí scáileán críochnaithe +Comment[gl]=O protector de pantalla rematou +Comment[gu]=સ્ક્રિન સેવર પૂર્ણ થયું છે +Comment[he]=שומר המסך הפסיק +Comment[hi]=स्क्रीन सेवर समाप्त हुआ +Comment[hr]=Zaštita zaslona je završila +Comment[hu]=A képernyővédő befejeződött +Comment[ia]=Le salvator de schermo ha finite +Comment[id]=Penyimpan layar telah selesai +Comment[is]=Skjáhvílan hefur lokið sér af +Comment[it]=Il salvaschermo si è concluso +Comment[ja]=スクリーンセーバーが終了しました +Comment[kk]=Экран қорғаушысы жұмысын тоқтатты +Comment[km]=បាន​បញ្ចប់​ធាតុ​រក្សា​អេក្រង់ +Comment[kn]=ತೆರೆ ರಕ್ಷಕ (ಸ್ಕ್ರೀನ್ ಸೇವರ್) ಅಂತ್ಯಗೊಂಡಿದೆ +Comment[ko]=화면 보호기 종료됨 +Comment[lt]=Ekrano užsklanda baigė darbą +Comment[lv]=Ekrāna saudzētājs tika apturēts +Comment[mk]=Чуварот на екранот заврши +Comment[ml]=സ്ക്രീന്‍ സേവര്‍ അവസാനിച്ചു +Comment[nb]=Pauseskjermen er ferdig +Comment[nds]=De Pausschirm wöör utmaakt +Comment[nl]=De schermbeveiliging is geëindigd +Comment[nn]=Pauseskjermen er ferdig +Comment[pa]=ਸਕਰੀਨ ਸੇਵਰ ਮੁਕੰਮਲ +Comment[pl]=Wygaszacz ekranu zakończył działanie +Comment[pt]=O protector de ecrã terminou +Comment[pt_BR]=O protetor de tela terminou +Comment[ro]=Protecția de ecran s-a încheiat +Comment[ru]=Хранитель экрана завершил работу +Comment[si]=තිරසුරැකුම අවසන් විය +Comment[sk]=Šetrič obrazovky bol ukončený +Comment[sl]=Ohranjevalnik zaslona se je zaključil +Comment[sr]=Чувар екрана се управо окончао +Comment[sr@ijekavian]=Чувар екрана се управо окончао +Comment[sr@ijekavianlatin]=Čuvar ekrana se upravo okončao +Comment[sr@latin]=Čuvar ekrana se upravo okončao +Comment[sv]=Skärmsläckaren har avslutats +Comment[th]=โปรแกรมรักษาจอภาพทำงานเสร็จสิ้นแล้ว +Comment[tr]=Ekran koruyucu bitti +Comment[ug]=ئېكران قوغدىغۇچ ئاخىرلاشتى +Comment[uk]=Роботу зберігача екрана завершено +Comment[wa]=Li spårgneu di waitroûle a cwité +Comment[x-test]=xxThe screen saver has finishedxx +Comment[zh_CN]=屏幕保护程序已运行完成 +Comment[zh_TW]=螢幕保護程式已完成 +Action=None + +[Event/unlocked] +Name=Screen unlocked +Name[ar]=فُكًَ قفل الشاشة +Name[ast]=Pantalla desbloquiada +Name[bg]=Екранът е отключен +Name[bs]=Ekran otključan +Name[ca]=Pantalla desbloquejada +Name[ca@valencia]=Pantalla desbloquejada +Name[cs]=Obrazovka odemknuta +Name[csb]=Ekran òdblokòwóny +Name[da]=Skærmen er låst op +Name[de]=Bildschirm freigegeben +Name[el]=Οθόνη ξεκλείδωτη +Name[en_GB]=Screen unlocked +Name[eo]=Ekrano malŝlosita +Name[es]=Pantalla desbloqueada +Name[et]=Ekraan on lahtilukustatud +Name[eu]=Pantaila desblokeatuta +Name[fi]=Näytön lukitus aukeni +Name[fr]=Écran déverrouillé +Name[fy]=Skerm ûntskoattele +Name[ga]=Scáileán díghlasáilte +Name[gl]=A pantalla desatrancouse +Name[gu]=સ્ક્રિનનું તાળું ખૂલેલ છે +Name[he]=המסך שוחרר +Name[hi]=स्क्रीन तालाबंद +Name[hr]=Zaslon otključan +Name[hu]=A képernyő feloldva +Name[ia]=Schermo disblocate +Name[id]=Layar tidak dikunci +Name[is]=Skjár aflæstur +Name[it]=Schermo sbloccato +Name[ja]=スクリーンロック解除 +Name[kk]=Экран бұғаты шешілді +Name[km]=បាន​​ដោះសោ​អេក្រង់ +Name[kn]=ತೆರೆಯನ್ನು ಅನ್ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ +Name[ko]=화면 잠금 풀림 +Name[lt]=Ekranas atrakintas +Name[lv]=Ekrāns atslēgts +Name[mk]=Екранот е отклучен +Name[ml]=സ്ക്രീന്‍ തുറന്നു +Name[nb]=Skjermen låst opp +Name[nds]=Schirm opslaten +Name[nl]=Scherm ontgrendeld +Name[nn]=Skjermen er låst opp +Name[pa]=ਸਕਰੀਨ ਅਣ-ਲਾਕ ਹੈ +Name[pl]=Ekran odblokowany +Name[pt]=Ecrã desbloqueado +Name[pt_BR]=Tela desbloqueada +Name[ro]=Ecran deblocat +Name[ru]=Экран разблокирован +Name[si]=තිරය අගුළු හැරිනි +Name[sk]=Obrazovka odomknutá +Name[sl]=Odklep zaslona +Name[sr]=Екран откључан +Name[sr@ijekavian]=Екран откључан +Name[sr@ijekavianlatin]=Ekran otključan +Name[sr@latin]=Ekran otključan +Name[sv]=Skärm upplåst +Name[tg]=Экран кушода шуд +Name[th]=ปลดล็อคหน้าจอแล้ว +Name[tr]=Ekranın kilidi açıldı +Name[ug]=ئېكران قۇلۇپسىزلاندى +Name[uk]=Екран розблоковано +Name[wa]=Waitroûle dizeclawêye +Name[x-test]=xxScreen unlockedxx +Name[zh_CN]=屏幕已解锁 +Name[zh_TW]=螢幕已解除鎖定 +Comment=The screen has been unlocked +Comment[ar]=تم قفل الشاشة +Comment[ast]=Desbloquióse la pantalla +Comment[bg]=Екранът е отключен +Comment[bs]=Ekran je upravo otključan +Comment[ca]=S'ha desbloquejat la pantalla +Comment[ca@valencia]=S'ha desbloquejat la pantalla +Comment[cs]=Obrazovka byla odemknuta +Comment[csb]=Ekran òstôł òdblokòwóny +Comment[da]=Skærmen er blevet låst op +Comment[de]=Der Bildschirm wurde freigegeben. +Comment[el]=Η οθόνη έχει ξεκλειδωθεί +Comment[en_GB]=The screen has been unlocked +Comment[eo]=La ekrano malŝlosiĝis +Comment[es]=Se ha desbloqueado la pantalla +Comment[et]=Ekraan on lahtilukustatud +Comment[eu]=Pantaila desblokeatu da +Comment[fi]=Näytön lukitus on avautunut +Comment[fr]=L'écran a été déverrouillé +Comment[fy]=It skerm is ûntskoattele +Comment[ga]=Tá an scáileán díghlasáilte +Comment[gl]=A pantalla desatrancouse +Comment[he]=המסך שוחרר +Comment[hr]=Zaslon je otključan +Comment[hu]=A képernyő feloldva +Comment[ia]=Le schermo ha essite disblocate +Comment[id]=Layar telah tidak dikunci +Comment[is]=Skjánum hefur verið aflæst +Comment[it]=Lo schermo è stato sbloccato +Comment[ja]=スクリーンのロックが解除されました +Comment[kk]=Экраннан бұғаты шешілді +Comment[km]=ធាតុរក្សាអេក្រង់​ត្រូ​វបានដោះសោ +Comment[kn]=ತೆರೆಯನ್ನು ಅನ್ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ +Comment[ko]=화면 잠금 풀림 +Comment[lt]=Ekranas buvo atrakintas +Comment[lv]=Ekrāns tika atslēgts +Comment[mk]=Екранот беше отклучен +Comment[ml]=സ്ക്രീന്‍ തുറന്നിരിയ്ക്കുന്നു +Comment[nb]=Skjermen er blitt låst opp +Comment[nds]=De Schirm wöör opslaten +Comment[nl]=Het scherm is ontgrendeld +Comment[nn]=Skjermen er låst opp +Comment[pa]=ਸਕਰੀਨ ਨੂੰ ਅਣ-ਲਾਕ ਕੀਤਾ ਗਿਆ +Comment[pl]=Ekran został odblokowany +Comment[pt]=O ecrã foi desbloqueado +Comment[pt_BR]=A tela foi desbloqueada +Comment[ro]=Ecranul a fost deblocat +Comment[ru]=Экран разблокирован +Comment[si]=තිරය අගුළු හැර ඇත +Comment[sk]=Obrazovka bola odomknutá +Comment[sl]=Zaslon je bil odklenjen +Comment[sr]=Екран је управо откључан +Comment[sr@ijekavian]=Екран је управо откључан +Comment[sr@ijekavianlatin]=Ekran je upravo otključan +Comment[sr@latin]=Ekran je upravo otključan +Comment[sv]=Skärmen har låsts upp +Comment[tg]=Клавиша модификатора зафиксирована +Comment[th]=หน้าจอถูกปลดล็อคแล้ว +Comment[tr]=Ekranın kilidi açıldı +Comment[ug]=ئېكران قۇلۇپسىزلانغان +Comment[uk]=Екран було розблоковано +Comment[wa]=Li waitroûle a stî dizeclawêye +Comment[x-test]=xxThe screen has been unlockedxx +Comment[zh_CN]=屏幕已经被解锁 +Comment[zh_TW]=螢幕已解除鎖定 +Action=None + +[Event/unlockfailed] +Name=Screen unlock failed +Name[ar]=فشل فك قفل الشاشة +Name[ast]=Nun pudo desbloquiase la pantalla +Name[bg]=Грешка при отключване на екрана +Name[bs]=Otključavanje ekrana neuspelo +Name[ca]=Ha fallat el desbloqueig de la pantalla +Name[ca@valencia]=Ha fallat el desbloqueig de la pantalla +Name[cs]=Odemknutí obrazovky selhalo +Name[csb]=Felënk òdblokòwaniô ekranu +Name[da]=Det mislykkedes at låse skærmen op +Name[de]=Entsperren des Bildschirms fehlgeschlagen +Name[el]=Το ξεκλειδωμα της οθόνης απέτυχε +Name[en_GB]=Screen unlock failed +Name[eo]=Malŝlositado de ekrano fiaskis +Name[es]=No se pudo desbloquear la pantalla +Name[et]=Ekraani lahtilukustamine nurjus +Name[eu]=Pantaila desblokeatzeak huts egin du +Name[fi]=Näytön lukinnan poisto epäonnistui +Name[fr]=Le déverrouillage de l'écran a échoué +Name[fy]=Skerm ûntskoatteling is mislearre +Name[ga]=Níorbh fhéidir an scáileán a dhíghlasáil +Name[gl]=Fallou o desbloqueo da pantalla +Name[gu]=સ્ક્રિનનું તાળું ખોલવામાં નિષ્ફળ +Name[he]=שיחרור המסך נכשל +Name[hi]=स्क्रीन ताला नहीं खुला +Name[hr]=Neuspjelo otključavanje zaslona +Name[hu]=Nem sikerült feloldani a képernyőt +Name[ia]=Disbloco de schermo falleva +Name[id]=Gagal membuka kunci +Name[is]=Aflæsing skjásins mistókst +Name[it]=Sblocco dello schermo non riuscito +Name[ja]=スクリーンのロック解除失敗 +Name[kk]=Экран бұғатын шешуі болмады +Name[km]=បាន​បរាជ័យ​ក្នុងការ​ដោះសោ​អេក្រង់ +Name[kn]=ತೆರೆಯನ್ನು ಅನ್ ಲಾಕ್ ಮಾಡುವುದು ವಿಫಲವಾಗಿದೆ +Name[ko]=화면 잠금 풀리지 않음 +Name[lt]=Ekrano atrakinimas nepavyko +Name[lv]=Neizdevās atslēgt ekrānu +Name[mk]=Не успеа отклучувањето на екранот +Name[ml]=സ്ക്രീന്‍ തുറക്കുന്നതില്‍ പരാജയപ്പെട്ടു +Name[nb]=Det lyktes ikke å låse opp skjermen +Name[nds]=Opsluten vun den Schirm fehlslaan +Name[nl]=Ontgrendelen van scherm is mislukt +Name[nn]=Klarte ikkje låsa opp skjermen +Name[pa]=ਸਕਰੀਨ ਖੋਲ੍ਹਣ ਲਈ ਫੇਲ੍ਹ +Name[pl]=Odblokowanie ekranu nieudane +Name[pt]=O desbloqueio do ecrã foi mal-sucedido +Name[pt_BR]=O bloqueio de tela falhou +Name[ro]=Deblocare ecranului a eșuat +Name[ru]=Не удалось разблокировать экран +Name[si]=තිරය අගුළු හැරීම අසාර්ථකයි +Name[sk]=Odomknutie obrazovky zlyhalo +Name[sl]=Spodletel odklep zaslona +Name[sr]=Откључавање екрана неуспело +Name[sr@ijekavian]=Откључавање екрана неуспјело +Name[sr@ijekavianlatin]=Otključavanje ekrana neuspjelo +Name[sr@latin]=Otključavanje ekrana neuspelo +Name[sv]=Upplåsning av skärm misslyckades +Name[tg]=Кушодани экран қатъ карда шуд +Name[th]=ล้มเหลวในการปลดล็อคหน้าจอ +Name[tr]=Ekran kilidi açılamadı +Name[ug]=ئېكران قۇلۇپسىزلاش مەغلۇپ بولدى +Name[uk]=Невдала спроба розблокування екрана +Name[wa]=Li dizeclawaedje del waitroûle a fwait berwete +Name[x-test]=xxScreen unlock failedxx +Name[zh_CN]=屏幕解锁失败 +Name[zh_TW]=螢幕解除鎖定失敗 +Comment=Failed attempt to unlock the screen +Comment[ar]=محاولة فاشلة لفك قفل الشاشة +Comment[ast]=Fallu al intentar desbloquiar la pantalla +Comment[bg]=Грешка при опит за отключване на екрана +Comment[bs]=Pokušaj otključavanja ekrana nije uspeo +Comment[ca]=L'intent de desbloquejar la pantalla ha fallat +Comment[ca@valencia]=L'intent de desbloquejar la pantalla ha fallat +Comment[cs]=Pokus o odemknutí obrazovky selhal +Comment[csb]=Felënk przëstãpù do òdblokòwaniô ekranu +Comment[da]=Mislykket forsøg på at låse skærmen op +Comment[de]=Das Entsperren des Bildschirms ist fehlgeschlagen. +Comment[el]=Αποτυχής προσπάθεια ξεκλειδώματος οθόνης +Comment[en_GB]=Failed attempt to unlock the screen +Comment[eo]=Provo de malŝlosi ekranon fiaskis +Comment[es]=Error al intentar desbloquear la pantalla +Comment[et]=Nurjunud katse ekraani lahti lukustada +Comment[eu]=Pantaila desblokeatzeko saiakerak huts egin du +Comment[fi]=Yritys näytön lukituksen poistamiseksi epäonnistui +Comment[fr]=Tentative infructueuse de déverrouiller l'écran +Comment[fy]=It is net slagge om it skerm te ûntskoatteljen +Comment[ga]=Theip ar iarracht an scáileán a dhíghlasáil +Comment[gl]=Fallou a tentativa de desbloquear a pantalla +Comment[he]=ניסיון כושל לשיחרור המסך +Comment[hr]=Propao je pokušaj otključavanja zaslona +Comment[hu]=Nem sikerült feloldani a képernyőt +Comment[ia]=Tentativa fallite de disblocar le schermo +Comment[id]=Gagal mencoba membuka kunci layar +Comment[is]=Tilraun til að aflæsa skjánum mistókst +Comment[it]=Tentanivo di sbloccare lo schermo non riuscito +Comment[ja]=スクリーンのロックを解除できませんでした +Comment[kk]=Экран бұғатын шешу әректі сәтсіз аяқталды +Comment[km]=បាន​បរាជ័យ​ក្នុង​ការ​ដោះសោ​អេក្រង់ +Comment[kn]=ತೆರೆಯನ್ನು ಅನ್ ಲಾಕ್ ಮಾಡುವ ವಿಫಲ ಯತ್ನ +Comment[ko]=화면 잠금 풀리지 않음 +Comment[lt]=Nepavyko bandymas atrakinti ekraną +Comment[lv]=Neizdevās atslēgt ekrānu +Comment[mk]=Не успеа обидот да се отклучи екранот +Comment[ml]=സ്ക്രീന്‍ തുറക്കാനുള്ള ശ്രമം പരാജയപ്പെട്ടിരിയ്ക്കുന്നു +Comment[nb]=Klarte ikke å låse opp skjermen +Comment[nds]=Opsluten vun den Schirm hett nich funkscheneert +Comment[nl]=Een mislukte poging om het scherm te ontgrendelen +Comment[nn]=Forsøket på å låsa opp skjermen var mislukka +Comment[pa]=ਸਕਰੀਨ ਅਣ-ਲਾਕ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਫੇਲ੍ਹ ਹੋਈ +Comment[pl]=Nieudana próba odblokowania ekranu +Comment[pt]=Falhou a tentativa de desbloquear o ecrã +Comment[pt_BR]=A tentativa de desbloquear a tela falhou +Comment[ro]=Încercare eșuată de a debloca ecranul +Comment[ru]=Не удалось разблокировать экран +Comment[si]=තිරය අගුළු හැරිමේ උත්සාහය අසාර්ථකයි +Comment[sk]=Neúspešný pokus o odomknutie obrazovky +Comment[sl]=Spodletel poskus odklepa zaslona +Comment[sr]=Покушај откључавања екрана није успео +Comment[sr@ijekavian]=Покушај откључавања екрана није успио +Comment[sr@ijekavianlatin]=Pokušaj otključavanja ekrana nije uspio +Comment[sr@latin]=Pokušaj otključavanja ekrana nije uspeo +Comment[sv]=Misslyckat försök att låsa upp skärmen +Comment[th]=ล้มเหลวในการพยายามปลดล็อคหน้าจอ +Comment[tr]=Ekran kilidini açma denemesi başarısız oldu +Comment[ug]=ئېكراننى قۇلۇپسىزلاندۇرالمىدى +Comment[uk]=Спроба розблокування екрана завершилася невдало +Comment[wa]=Li saye di dizeclawaedje del waitroûle a fwait berwete +Comment[x-test]=xxFailed attempt to unlock the screenxx +Comment[zh_CN]=尝试解锁屏幕失败 +Comment[zh_TW]=螢幕解除鎖定失敗 +Action=None diff --git a/kwin/screenlocker/lock/lockdlg.cc b/kwin/screenlocker/lock/lockdlg.cc new file mode 100644 index 0000000..14a9b34 --- /dev/null +++ b/kwin/screenlocker/lock/lockdlg.cc @@ -0,0 +1,667 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Chris Howells +// Copyright 2003 Oswald Buddenhagen + +#include // HAVE_PAM + +#include "lockprocess.h" +#include "lockdlg.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 +#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 + +#ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +#endif + +#define PASSDLG_HIDE_TIMEOUT 10000 + +//=========================================================================== +// +// Simple dialog for entering a password. +// +PasswordDlg::PasswordDlg(LockProcess *parent, GreeterPluginHandle *plugin, const QString &text) + : KDialog(parent, Qt::X11BypassWindowManagerHint), + mPlugin( plugin ), + mCapsLocked(-1), + mUnlockingFailed(false), + sNot(0) +{ + QWidget* w = mainWidget(); + + QLabel *pixLabel = new QLabel( w ); + pixLabel->setPixmap(DesktopIcon(QLatin1String( "system-lock-screen" ))); + + KUser user; QString fullName=user.property(KUser::FullName).toString(); + QString greetString = text; + if (text.isEmpty()) { + greetString = fullName.isEmpty() ? + i18n("The session is locked
") : + i18n("The session was locked by %1
", fullName ); + } + QLabel *greetLabel = new QLabel(greetString, w); + + mStatusLabel = new QLabel( QLatin1String( " " ), w ); + mStatusLabel->setAlignment( Qt::AlignCenter ); + + greet = plugin->info->create(this, w, QString(), + KGreeterPlugin::Authenticate, + KGreeterPlugin::ExUnlock); + + KSeparator *sep = new KSeparator( Qt::Horizontal, w ); + + ok = new KPushButton( KGuiItem(i18n("Unl&ock"), QLatin1String( "object-unlocked" )), w ); + cancel = new KPushButton( KStandardGuiItem::cancel(), w ); + mNewSessButton = new KPushButton( KGuiItem(i18n("Sw&itch User..."), QLatin1String( "fork" )), w ); + + // Using keyboard layout component + KPluginFactory *kxkbFactory = KPluginLoader(QLatin1String( "keyboard_layout_widget" )).factory(); + QWidget *kxkbComponent = NULL; + if (kxkbFactory) { + kxkbComponent = kxkbFactory->create(this); + } + else { + kDebug() << "can't load keyboard layout widget library"; + } + + QHBoxLayout *layStatus = new QHBoxLayout(); + layStatus->addStretch(); + layStatus->addWidget( mStatusLabel ); + layStatus->addStretch(); + + if( kxkbComponent ) { + //TODO: without this the widget is off the parent area, but we need something better here + kxkbComponent->setFixedSize(48, 24); + layStatus->addWidget( kxkbComponent, 0, Qt::AlignRight ); + } + + QHBoxLayout *layButtons = new QHBoxLayout(); + layButtons->addWidget( mNewSessButton ); + layButtons->addStretch(); + layButtons->addWidget( ok ); + layButtons->addWidget( cancel ); + + frameLayout = new QGridLayout( w ); + frameLayout->setSpacing( KDialog::spacingHint() ); + frameLayout->setMargin( KDialog::marginHint() ); + frameLayout->addWidget( pixLabel, 0, 0, 3, 1, Qt::AlignTop ); + frameLayout->addWidget( greetLabel, 0, 1 ); + frameLayout->addWidget( greet->getWidgets().first(), 1, 1 ); + frameLayout->addLayout( layStatus, 2, 1 ); + frameLayout->addWidget( sep, 3, 0, 1, 2 ); + frameLayout->addLayout( layButtons, 4, 0, 1, 2 ); + + setButtons(None); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + connect(ok, SIGNAL(clicked()), SLOT(slotOK())); + connect(mNewSessButton, SIGNAL(clicked()), SLOT(slotSwitchUser())); + + if (!text.isEmpty() || !KDisplayManager().isSwitchable() || !KAuthorized::authorizeKAction(QLatin1String( "switch_user" ))) + mNewSessButton->hide(); + + installEventFilter(this); + + mFailedTimerId = 0; + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); + connect(qApp, SIGNAL(activity()), SLOT(slotActivity()) ); + + greet->start(); + + capsLocked(); +} + +PasswordDlg::~PasswordDlg() +{ + hide(); + delete greet; +} + +void PasswordDlg::updateLabel() +{ + if (mUnlockingFailed) + { + QPalette palette; + KColorScheme::adjustForeground(palette, KColorScheme::NormalText, QPalette::WindowText); + mStatusLabel->setPalette(palette); + mStatusLabel->setText(i18n("Unlocking failed")); + } + else + if (mCapsLocked) + { + QPalette palette = mStatusLabel->palette(); + KColorScheme::adjustForeground(palette, KColorScheme::NegativeText, QPalette::WindowText); + mStatusLabel->setPalette(palette); + mStatusLabel->setText(i18n("Warning: Caps Lock on")); + } + else + { + mStatusLabel->setText(QLatin1String( " " )); + } +} + +//--------------------------------------------------------------------------- +// +// Handle timer events. +// +void PasswordDlg::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == mTimeoutTimerId) + { + done(TIMEOUT_CODE); + } + else if (ev->timerId() == mFailedTimerId) + { + killTimer(mFailedTimerId); + mFailedTimerId = 0; + // Show the normal password prompt. + mUnlockingFailed = false; + updateLabel(); + ok->setEnabled(true); + cancel->setEnabled(true); + mNewSessButton->setEnabled( true ); + greet->revive(); + greet->start(); + } +} + +bool PasswordDlg::eventFilter(QObject *, QEvent *ev) +{ + if (ev->type() == QEvent::KeyPress || ev->type() == QEvent::KeyRelease) + capsLocked(); + return false; +} + +void PasswordDlg::slotActivity() +{ + if (mTimeoutTimerId) { + killTimer(mTimeoutTimerId); + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); + } +} + +////// kckeckpass interface code + +int PasswordDlg::Reader (void *buf, int count) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = ::read (sFd, (void *)((char *)buf + rlen), count - rlen); + if (ret < 0) { + if (errno == EINTR) + goto dord; + if (errno == EAGAIN) + break; + return -1; + } + if (!ret) + break; + rlen += ret; + } + return rlen; +} + +bool PasswordDlg::GRead (void *buf, int count) +{ + return Reader (buf, count) == count; +} + +bool PasswordDlg::GWrite (const void *buf, int count) +{ + return ::write (sFd, buf, count) == count; +} + +bool PasswordDlg::GSendInt (int val) +{ + return GWrite (&val, sizeof(val)); +} + +bool PasswordDlg::GSendStr (const char *buf) +{ + int len = buf ? ::strlen (buf) + 1 : 0; + return GWrite (&len, sizeof(len)) && GWrite (buf, len); +} + +bool PasswordDlg::GSendArr (int len, const char *buf) +{ + return GWrite (&len, sizeof(len)) && GWrite (buf, len); +} + +bool PasswordDlg::GRecvInt (int *val) +{ + return GRead (val, sizeof(*val)); +} + +bool PasswordDlg::GRecvArr (char **ret) +{ + int len; + char *buf; + + if (!GRecvInt(&len)) + return false; + if (!len) { + *ret = 0; + return true; + } + if (!(buf = (char *)::malloc (len))) + return false; + *ret = buf; + if (GRead (buf, len)) { + return true; + } else { + ::free(buf); + *ret = 0; + return false; + } +} + +void PasswordDlg::reapVerify() +{ + sNot->setEnabled( false ); + sNot->deleteLater(); + sNot = 0; + ::close( sFd ); + int status; + while (::waitpid( sPid, &status, 0 ) < 0) + if (errno != EINTR) { // This should not happen ... + cantCheck(); + return; + } + if (WIFEXITED(status)) + switch (WEXITSTATUS(status)) { + case AuthOk: + greet->succeeded(); + accept(); + return; + case AuthBad: + greet->failed(); + mUnlockingFailed = true; + updateLabel(); + mFailedTimerId = startTimer(1500); + ok->setEnabled(false); + cancel->setEnabled(false); + mNewSessButton->setEnabled( false ); + KNotification::event( QLatin1String( "unlockfailed" ) ); + return; + case AuthAbort: + return; + } + cantCheck(); +} + +void PasswordDlg::handleVerify() +{ + int ret; + char *arr; + + if (GRecvInt( &ret )) { + switch (ret) { + case ConvGetBinary: + if (!GRecvArr( &arr )) + break; + greet->binaryPrompt( arr, false ); + if (arr) + ::free( arr ); + return; + case ConvGetNormal: + if (!GRecvArr( &arr )) + break; + greet->textPrompt( arr, true, false ); + if (arr) + ::free( arr ); + return; + case ConvGetHidden: + if (!GRecvArr( &arr )) + break; + greet->textPrompt( arr, false, false ); + if (arr) + ::free( arr ); + return; + case ConvPutInfo: + if (!GRecvArr( &arr )) + break; + if (!greet->textMessage( arr, false )) + static_cast< LockProcess* >(parent())->msgBox( this, QMessageBox::Information, QString::fromLocal8Bit( arr ) ); + ::free( arr ); + return; + case ConvPutError: + if (!GRecvArr( &arr )) + break; + if (!greet->textMessage( arr, true )) + static_cast< LockProcess* >(parent())->msgBox( this, QMessageBox::Warning, QString::fromLocal8Bit( arr ) ); + ::free( arr ); + return; + } + } + reapVerify(); +} + +////// greeter plugin callbacks + +void PasswordDlg::gplugReturnText( const char *text, int tag ) +{ + GSendStr( text ); + if (text) + GSendInt( tag ); +} + +void PasswordDlg::gplugReturnBinary( const char *data ) +{ + if (data) { + unsigned const char *up = (unsigned const char *)data; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + if (!len) + GSendArr( 4, data ); + else + GSendArr( len, data ); + } else + GSendArr( 0, 0 ); +} + +void PasswordDlg::gplugSetUser( const QString & ) +{ + // ignore ... +} + +void PasswordDlg::cantCheck() +{ + greet->failed(); + static_cast< LockProcess* >(parent())->msgBox( this, QMessageBox::Critical, + i18n("Cannot unlock the session because the authentication system failed to work;\n" + "you must kill kscreenlocker (pid %1) manually.", getpid()) ); + greet->revive(); +} + +//--------------------------------------------------------------------------- +// +// Starts the kcheckpass process to check the user's password. +// +void PasswordDlg::gplugStart() +{ + int sfd[2]; + char fdbuf[16]; + + if (sNot) + return; + if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) { + cantCheck(); + return; + } + if ((sPid = ::fork()) < 0) { + ::close(sfd[0]); + ::close(sfd[1]); + cantCheck(); + return; + } + if (!sPid) { + ::close(sfd[0]); + sprintf(fdbuf, "%d", sfd[1]); + execlp(QFile::encodeName(KStandardDirs::findExe(QLatin1String( "kcheckpass" ))).data(), + "kcheckpass", + "-m", mPlugin->info->method, + "-S", fdbuf, + (char *)0); + _exit(20); + } + ::close(sfd[1]); + sFd = sfd[0]; + sNot = new QSocketNotifier(sFd, QSocketNotifier::Read, this); + connect(sNot, SIGNAL(activated(int)), SLOT(handleVerify())); +} + +void PasswordDlg::gplugChanged() +{ +} + +void PasswordDlg::gplugActivity() +{ + slotActivity(); +} + +void PasswordDlg::gplugMsgBox( QMessageBox::Icon type, const QString &text ) +{ + static_cast< LockProcess* >(parent())->msgBox( this, type, text ); +} + +bool PasswordDlg::gplugHasNode( const QString & ) +{ + return false; +} + +void PasswordDlg::slotOK() +{ + greet->next(); +} + + +void PasswordDlg::setVisible( bool visible ) +{ + QDialog::setVisible( visible ); + + if ( visible ) + QApplication::flush(); +} + +void PasswordDlg::slotStartNewSession() +{ + if (!KMessageBox::shouldBeShownContinue( QLatin1String( ":confirmNewSession" ) )) { + KDisplayManager().startReserve(); + return; + } + + killTimer(mTimeoutTimerId); + mTimeoutTimerId = 0; + + KDialog *dialog = new KDialog( this, Qt::X11BypassWindowManagerHint ); + dialog->setModal( true ); + dialog->setButtons( KDialog::Yes | KDialog::No ); + dialog->setButtonGuiItem( KDialog::Yes, KGuiItem(i18n("&Start New Session"), QLatin1String( "fork" )) ); + dialog->setButtonGuiItem( KDialog::No, KStandardGuiItem::cancel() ); + dialog->setDefaultButton( KDialog::Yes ); + dialog->setEscapeButton( KDialog::No ); + + bool dontAskAgain = false; + + KMessageBox::createKMessageBox( dialog, QMessageBox::Warning, + i18n("You have chosen to open another desktop session " + "instead of resuming the current one.\n" + "The current session will be hidden " + "and a new login screen will be displayed.\n" + "An F-key is assigned to each session; " + "F%1 is usually assigned to the first session, " + "F%2 to the second session and so on. " + "You can switch between sessions by pressing " + "Ctrl, Alt and the appropriate F-key at the same time. " + "Additionally, the KDE Panel and Desktop menus have " + "actions for switching between sessions.", + 7, 8), + QStringList(), + i18n("&Do not ask again"), &dontAskAgain, + KMessageBox::NoExec ); + + int ret = static_cast< LockProcess* >( parent())->execDialog( dialog ); + + delete dialog; + + if (ret == KDialog::Yes) { + if (dontAskAgain) + KMessageBox::saveDontShowAgainContinue( QLatin1String( ":confirmNewSession" ) ); + KDisplayManager().startReserve(); + } + + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); +} + +class LockListViewItem : public QTreeWidgetItem { +public: + LockListViewItem( QTreeWidget *parent, + const QString &sess, const QString &loc, int _vt ) + : QTreeWidgetItem( parent ) + , vt( _vt ) + { + setText( 0, sess ); + setText( 1, loc ); + } + + int vt; +}; + +void PasswordDlg::slotSwitchUser() +{ + int p = 0; + KDisplayManager dm; + + QDialog dialog( this, Qt::X11BypassWindowManagerHint ); + dialog.setModal( true ); + + QBoxLayout *hbox = new QHBoxLayout( &dialog ); + hbox->setSpacing( KDialog::spacingHint() ); + hbox->setMargin( KDialog::marginHint() ); + + QBoxLayout *vbox1 = new QVBoxLayout( ); + hbox->addItem( vbox1 ); + QBoxLayout *vbox2 = new QVBoxLayout( ); + hbox->addItem( vbox2 ); + + KPushButton *btn; + + SessList sess; + if (dm.localSessions( sess )) { + + lv = new QTreeWidget( &dialog ); + connect( lv, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), SLOT(slotSessionActivated()) ); + connect( lv, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), &dialog, SLOT(reject()) ); + lv->setAllColumnsShowFocus( true ); + lv->setHeaderLabels( QStringList() << i18n("Session") << i18n("Location") ); + lv->header()->setResizeMode( 0, QHeaderView::Stretch ); + lv->header()->setResizeMode( 1, QHeaderView::Stretch ); + QTreeWidgetItem *itm = 0; + QString user, loc; + int ns = 0; + for (SessList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) { + KDisplayManager::sess2Str2( *it, user, loc ); + itm = new LockListViewItem( lv, user, loc, (*it).vt ); + if (!(*it).vt) + itm->setFlags( itm->flags() & ~Qt::ItemIsEnabled ); + if ((*it).self) { + lv->setCurrentItem( itm ); + itm->setSelected( true ); + } + ns++; + } + int fw = lv->frameWidth() * 2; + QSize hds( lv->header()->sizeHint() ); + lv->setMinimumWidth( fw + hds.width() + + (ns > 10 ? style()->pixelMetric(QStyle::PM_ScrollBarExtent) : 0 ) ); + int ih = lv->itemDelegate()->sizeHint( + QStyleOptionViewItem(), lv->model()->index( 0, 0 ) ).height(); + lv->setFixedHeight( fw + hds.height() + + ih * (ns < 6 ? 6 : ns > 10 ? 10 : ns) ); + lv->header()->adjustSize(); + vbox1->addWidget( lv ); + + btn = new KPushButton( KGuiItem(i18nc("session", "&Activate"), QLatin1String( "fork" )), &dialog ); + connect( btn, SIGNAL(clicked()), SLOT(slotSessionActivated()) ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + vbox2->addWidget( btn ); + vbox2->addStretch( 2 ); + } + + if (KAuthorized::authorizeKAction(QLatin1String( "start_new_session" )) && (p = dm.numReserve()) >= 0) + { + btn = new KPushButton( KGuiItem(i18n("Start &New Session"), QLatin1String( "fork" )), &dialog ); + connect( btn, SIGNAL(clicked()), SLOT(slotStartNewSession()) ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + if (!p) + btn->setEnabled( false ); + vbox2->addWidget( btn ); + vbox2->addStretch( 1 ); + } + + btn = new KPushButton( KStandardGuiItem::cancel(), &dialog ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + vbox2->addWidget( btn ); + + static_cast< LockProcess* >(parent())->execDialog( &dialog ); +} + +void PasswordDlg::slotSessionActivated() +{ + LockListViewItem *itm = (LockListViewItem *)lv->currentItem(); + if (itm && itm->vt > 0) + KDisplayManager().switchVT( itm->vt ); +} + +void PasswordDlg::capsLocked() +{ + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer(QX11Info::display(), DefaultRootWindow( QX11Info::display() ), &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &lmask); + mCapsLocked = lmask & LockMask; + updateLabel(); +} + +#include "lockdlg.moc" diff --git a/kwin/screenlocker/lock/lockdlg.h b/kwin/screenlocker/lock/lockdlg.h new file mode 100644 index 0000000..f25e55f --- /dev/null +++ b/kwin/screenlocker/lock/lockdlg.h @@ -0,0 +1,96 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Oswald Buddenhagen +// + +#ifndef __LOCKDLG_H__ +#define __LOCKDLG_H__ + +#include + +#include + +#include +#include +#include +#include +#include + +struct GreeterPluginHandle; +class LockProcess; +class QFrame; +class QGridLayout; +class QLabel; +class KPushButton; +class QSocketNotifier; +class QTreeWidget; + +//=========================================================================== +// +// Simple dialog for entering a password. +// It does not handle password validation. +// +class PasswordDlg : public KDialog, public KGreeterPluginHandler +{ + Q_OBJECT + +public: + PasswordDlg(LockProcess *parent, GreeterPluginHandle *plugin, const QString &text = QString()); + ~PasswordDlg(); + virtual void setVisible(bool visible); + + // from KGreetPluginHandler + virtual void gplugReturnText( const char *text, int tag ); + virtual void gplugReturnBinary( const char *data ); + virtual void gplugSetUser( const QString & ); + virtual void gplugStart(); + virtual void gplugChanged(); + virtual void gplugActivity(); + virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text ); + virtual bool gplugHasNode( const QString &id ); + +protected: + virtual void timerEvent(QTimerEvent *); + virtual bool eventFilter(QObject *, QEvent *); + +private Q_SLOTS: + void slotSwitchUser(); + void slotSessionActivated(); + void slotStartNewSession(); + void slotOK(); + void slotActivity(); + void handleVerify(); + +private: + void capsLocked(); + void updateLabel(); + int Reader (void *buf, int count); + bool GRead (void *buf, int count); + bool GWrite (const void *buf, int count); + bool GSendInt (int val); + bool GSendStr (const char *buf); + bool GSendArr (int len, const char *buf); + bool GRecvInt (int *val); + bool GRecvArr (char **buf); + void reapVerify(); + void cantCheck(); + GreeterPluginHandle *mPlugin; + KGreeterPlugin *greet; + QFrame *frame; + QGridLayout *frameLayout; + QLabel *mStatusLabel; + KPushButton *mNewSessButton, *ok, *cancel; + int mFailedTimerId; + int mTimeoutTimerId; + int mCapsLocked; + bool mUnlockingFailed; + int sPid, sFd; + QSocketNotifier *sNot; + QTreeWidget *lv; +}; + +#endif + diff --git a/kwin/screenlocker/lock/lockprocess.cc b/kwin/screenlocker/lock/lockprocess.cc new file mode 100644 index 0000000..65c7f1d --- /dev/null +++ b/kwin/screenlocker/lock/lockprocess.cc @@ -0,0 +1,1808 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Oswald Buddenhagen +// Copyright 2008 Chani Armitage +// + +//krunner keeps running and checks user inactivity +//when it should show screensaver (and maybe lock the session), +//it starts kscreenlocker, who does all the locking and who +//actually starts the screensaver + +//It's done this way to prevent screen unlocking when krunner +//crashes + +#include "lockprocess.h" +#include "lockprocessadaptor.h" + +#include +#include +#include +#include "lockdlg.h" +#include "autologout.h" +#include "kscreensaversettings.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 +#include +#include +#include +#include +#include +#include +#include + +#include // % operator for QString + +#include + +#include +#include +#include +#include +#ifdef HAVE_SETPRIORITY +#include +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_DPMS +extern "C" { +#include +#ifndef Bool +#define Bool BOOL +#endif +#include + +#ifndef HAVE_DPMSINFO_PROTO +Status DPMSInfo ( Display *, CARD16 *, BOOL * ); +#endif +} +#endif + +#ifdef HAVE_XF86MISC +#include +#endif + +#ifdef HAVE_GLXCHOOSEVISUAL +#include +#endif + +#define LOCK_GRACE_DEFAULT 5000 +#define AUTOLOGOUT_DEFAULT 600 + +static Window gVRoot = 0; +static Window gVRootData = 0; +static Atom gXA_VROOT; +static Atom gXA_SCREENSAVER_VERSION; + +//#define CHECK_XSELECTINPUT +#ifdef CHECK_XSELECTINPUT +#include +static bool check_xselectinput = false; +extern "C" +int XSelectInput( Display* dpy, Window w, long e ) +{ + typedef int (*ptr)(Display*, Window, long); + static ptr fun = NULL; + if( fun == NULL ) + fun = (ptr)dlsym( RTLD_NEXT, "XSelectInput" ); + if( check_xselectinput && w == DefaultRootWindow( dpy )) + kDebug() << kBacktrace(); + return fun( dpy, w, e ); +} +#endif + +static QLatin1String s_overlayServiceName("org.kde.plasma-overlay"); + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry.f +// +LockProcess::LockProcess(bool child, bool useBlankOnly) + : QWidget(0L, Qt::X11BypassWindowManagerHint), + mInitialLock(false), + mLocked(false), + mBusy(false), + mPlasmaDBus(0), + mServiceWatcher(0), + mSetupMode(false), + mOpenGLVisual(false), + child_saver(child), + mParent(0), + mUseBlankOnly(useBlankOnly), + mSuspended(false), + mVisibility(false), + mEventRecursed(false), + mRestoreXF86Lock(false), + mForbidden(false), + mAutoLogoutTimerId(0) +{ + setObjectName(QLatin1String( "save window" )); + setupSignals(); + + new LockProcessAdaptor(this); + QDBusConnection::sessionBus().registerService(QLatin1String( "org.kde.screenlocker" )); + QDBusConnection::sessionBus().registerObject(QLatin1String( "/LockProcess" ), this); + + kapp->installX11EventFilter(this); + + // Get root window size + XWindowAttributes rootAttr; + QX11Info info; + XGetWindowAttributes(QX11Info::display(), RootWindow(QX11Info::display(), + info.screen()), &rootAttr); + kapp->desktop(); // make Qt set its event mask on the root window first + XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), + SubstructureNotifyMask | rootAttr.your_event_mask ); +#ifdef CHECK_XSELECTINPUT + check_xselectinput = true; +#endif + setGeometry(0, 0, rootAttr.width, rootAttr.height); + + // virtual root property + gXA_VROOT = XInternAtom (QX11Info::display(), "__SWM_VROOT", False); + gXA_SCREENSAVER_VERSION = XInternAtom (QX11Info::display(), "_SCREENSAVER_VERSION", False); + + connect(&mHackProc, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(hackExited())); + + mSuspendTimer.setSingleShot(true); + connect(&mSuspendTimer, SIGNAL(timeout()), SLOT(suspend())); + + const QStringList dmopt = + QString::fromLatin1( ::getenv( "XDM_MANAGED" )).split(QLatin1Char(','), QString::SkipEmptyParts); + for (QStringList::ConstIterator it = dmopt.constBegin(); it != dmopt.constEnd(); ++it) + if ((*it).startsWith(QLatin1String( "method=" ))) + mMethod = (*it).mid(7); + + configure(); + +#ifdef HAVE_DPMS + if (mDPMSDepend) { + BOOL on; + CARD16 state; + DPMSInfo(QX11Info::display(), &state, &on); + if (on) + { + connect(&mCheckDPMS, SIGNAL(timeout()), SLOT(checkDPMSActive())); + // we can save CPU if we stop it as quickly as possible + // but we waste CPU if we check too often -> so take 10s + mCheckDPMS.start(10000); + } + } +#endif + + greetPlugin.library = 0; + + mSuppressUnlock.setSingleShot(true); + connect(&mSuppressUnlock, SIGNAL(timeout()), SLOT(deactivatePlasma())); + + // read the initial information about all toplevel windows + Window r, p; + Window* real; + unsigned nreal; + if( XQueryTree( x11Info().display(), x11Info().appRootWindow(), &r, &p, &real, &nreal ) + && real != NULL ) { + KXErrorHandler err; // ignore X errors here + for( unsigned i = 0; i < nreal; ++i ) { + XWindowAttributes winAttr; + if (XGetWindowAttributes(QX11Info::display(), real[ i ], &winAttr)) { + WindowInfo info; + info.window = real[ i ]; + info.viewable = ( winAttr.map_state == IsViewable ); + windowInfo.append( info ); // ordered bottom to top + } + } + XFree( real ); + } +} + +//--------------------------------------------------------------------------- +// +// Destructor - usual cleanups. +// +LockProcess::~LockProcess() +{ + if (greetPlugin.library) { + if (greetPlugin.info->done) + greetPlugin.info->done(); + greetPlugin.library->unload(); + } +} + +static int signal_pipe[2]; + +static void sigterm_handler(int) +{ + char tmp = 'T'; + ::write( signal_pipe[1], &tmp, 1); +} + +static void sighup_handler(int) +{ + char tmp = 'H'; + ::write( signal_pipe[1], &tmp, 1); +} + +static void sigusr1_handler(int) +{ + char tmp = '1'; + ::write(signal_pipe[1], &tmp, 1); +} + +void LockProcess::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == mAutoLogoutTimerId) + { + AutoLogout autologout(this); + execDialog(&autologout); + } +} + +void LockProcess::setupSignals() +{ + struct sigaction act; + sigemptyset(&(act.sa_mask)); + act.sa_flags = 0; + // ignore SIGINT + act.sa_handler=SIG_IGN; + sigaction(SIGINT, &act, 0L); + // ignore SIGQUIT + //act.sa_handler=SIG_IGN; + sigaction(SIGQUIT, &act, 0L); + // exit cleanly on SIGTERM + act.sa_handler= sigterm_handler; + sigaction(SIGTERM, &act, 0L); + // SIGHUP forces lock + act.sa_handler= sighup_handler; + sigaction(SIGHUP, &act, 0L); + // SIGUSR1 simulates user activity + act.sa_handler= sigusr1_handler; + sigaction(SIGUSR1, &act, 0L); + + pipe(signal_pipe); + QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], QSocketNotifier::Read, this); + connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal())); +} + + +void LockProcess::signalPipeSignal() +{ + char tmp; + ::read( signal_pipe[0], &tmp, 1); + if (tmp == 'T') { + quitSaver(); + } else if (tmp == '1') { + // In case SimulateUserActivity (SIGUSR1) is called during the dead-time (mBusy == true). + mInitialLock = true; + if (!mBusy && mDialogs.isEmpty()) { + mBusy = true; + quit(); + mBusy = false; + } + } else if (tmp == 'H') { + if( !mLocked ) + startLock(); + } +} + +//--------------------------------------------------------------------------- +bool LockProcess::lock(bool initial) +{ + if (startSaver()) { + // In case of a forced lock we don't react to events during + // the dead-time to give the screensaver some time to activate. + // That way we don't accidentally show the password dialog before + // the screensaver kicks in because the user moved the mouse after + // selecting "lock screen", that looks really untidy. + mBusy = true; + mInitialLock = initial; + if (startLock()) + { + QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed())); + return true; + } + stopSaver(); + mBusy = false; + } + return false; +} +//--------------------------------------------------------------------------- +void LockProcess::slotDeadTimePassed() +{ + if (mInitialLock) + quit(); + mBusy = false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::defaultSave() +{ + mLocked = false; + if (startSaver()) { + if (mLockGrace >= 0) + QTimer::singleShot(mLockGrace, this, SLOT(startLock())); + return true; + } + return false; +} + +bool LockProcess::startSetup() +{ + mPlasmaEnabled = true; //force it on in case the user didn't click apply yet + mLocked = false; + mSetupMode = true; + return startSaver(); + //plasma startup will handle the suppressunlock bit +} +//--------------------------------------------------------------------------- +bool LockProcess::dontLock() +{ + mLocked = false; + return startSaver(); +} + +//--------------------------------------------------------------------------- +void LockProcess::quitSaver() +{ + stopSaver(); + qApp->quit(); +} + +//--------------------------------------------------------------------------- +// +// Read and apply configuration. +// +void LockProcess::configure() +{ + // the configuration is stored in krunner's config file + if( KScreenSaverSettings::lock() ) { + mLockGrace = KScreenSaverSettings::lockGrace(); + if (mLockGrace < 0) + mLockGrace = 0; + else if (mLockGrace > 300000) + mLockGrace = 300000; // 5 minutes, keep the value sane + } else { + mLockGrace = -1; + } + + mAutoLogoutTimeout = KScreenSaverSettings::autoLogout() ? + KScreenSaverSettings::autoLogoutTimeout() : 0; + +#ifdef HAVE_DPMS + mDPMSDepend = KScreenSaverSettings::suspendWhenInvisible(); +#endif + + mPriority = KScreenSaverSettings::priority(); + if (mPriority < 0) mPriority = 0; + if (mPriority > 19) mPriority = 19; + + mSaver = KScreenSaverSettings::saver(); + if (mSaver.isEmpty() || mUseBlankOnly) { + mSaver = QLatin1String( "kblank.desktop" ); + } + + readSaver(); + + mPlasmaEnabled = KScreenSaverSettings::plasmaEnabled(); + + mSuppressUnlockTimeout = qMax(0, KScreenSaverSettings::timeout() * 1000); + mSuppressUnlockTimeout = qMax(mSuppressUnlockTimeout, 30 * 1000); //min. 30 secs FIXME is this a good idea? + + mPlugins = KScreenSaverSettings::pluginsUnlock(); + if (mPlugins.isEmpty()) { + mPlugins << QLatin1String( "classic" ) << QLatin1String( "generic" ); + } + mPluginOptions = KScreenSaverSettings::pluginOptions(); +} + +//--------------------------------------------------------------------------- +// +// Read the command line needed to run the screensaver given a .desktop file. +// +void LockProcess::readSaver() +{ + if (!mSaver.isEmpty()) + { + QString entryName = mSaver; + if( entryName.endsWith( QLatin1String( ".desktop" ) )) + entryName = entryName.left( entryName.length() - 8 ); // strip it + const KService::List offers = KServiceTypeTrader::self()->query( QLatin1String( "ScreenSaver" ), + QLatin1String( "DesktopEntryName == '" ) + entryName.toLower() + QLatin1Char( '\'' ) ); + if( offers.isEmpty() ) + { + kDebug(1204) << "Cannot find screesaver: " << mSaver; + return; + } + const QString file = KStandardDirs::locate("services", offers.first()->entryPath()); + + const bool opengl = KAuthorized::authorizeKAction(QLatin1String( "opengl_screensavers" )); + const bool manipulatescreen = KAuthorized::authorizeKAction(QLatin1String( "manipulatescreen_screensavers" )); + KDesktopFile config( file ); + KConfigGroup desktopGroup = config.desktopGroup(); + foreach (const QString &type, desktopGroup.readEntry("X-KDE-Type").split(QLatin1Char(';'))) { + if (type == QLatin1String("ManipulateScreen")) { + if (!manipulatescreen) { + kDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden"; + mForbidden = true; + } + } else if (type == QLatin1String("OpenGL")) { + mOpenGLVisual = true; + if (!opengl) { + kDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden"; + mForbidden = true; + } + } + } + + kDebug(1204) << "mForbidden: " << (mForbidden ? "true" : "false"); + + if (config.hasActionGroup(QLatin1String( "Root" ))) + { + mSaverExec = config.actionGroup(QLatin1String( "Root" )).readPathEntry("Exec", QString()); + } + } +} + +//--------------------------------------------------------------------------- +// +// Create a window to draw our screen saver on. +// +void LockProcess::createSaverWindow() +{ + Visual* visual = CopyFromParent; + int depth = CopyFromParent; + XSetWindowAttributes attrs; + int flags = CWOverrideRedirect; +#ifdef HAVE_GLXCHOOSEVISUAL +// this code is (partially) duplicated in kdebase/workspace/kcontrol/screensaver + if( mOpenGLVisual ) + { + static const int attribs[][ 15 ] = + { + #define R GLX_RED_SIZE + #define G GLX_GREEN_SIZE + #define B GLX_BLUE_SIZE + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, None } + #undef R + #undef G + #undef B + }; + for( unsigned int i = 0; + i < sizeof( attribs ) / sizeof( attribs[ 0 ] ); + ++i ) + { + if( XVisualInfo* info = glXChooseVisual( x11Info().display(), x11Info().screen(), const_cast(attribs[ i ]) )) + { + visual = info->visual; + depth = info->depth; + static Colormap colormap = 0; + if( colormap != 0 ) + XFreeColormap( x11Info().display(), colormap ); + colormap = XCreateColormap( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), visual, AllocNone ); + attrs.colormap = colormap; + flags |= CWColormap; + XFree( info ); + break; + } + } + } +#endif + attrs.override_redirect = 1; + hide(); + Window w = XCreateWindow( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), + x(), y(), width(), height(), 0, depth, InputOutput, visual, flags, &attrs ); + + create( w, false, true ); + + // Some xscreensaver hacks check for this property + const char *version = "KDE 4.0"; + XChangeProperty (QX11Info::display(), winId(), + gXA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace, + (unsigned char *) version, strlen(version)); + + + XSetWindowAttributes attr; + attr.event_mask = KeyPressMask | ButtonPressMask | PointerMotionMask | + VisibilityChangeMask | ExposureMask; + XChangeWindowAttributes(QX11Info::display(), winId(), + CWEventMask, &attr); + + // erase(); + + // set NoBackground so that the saver can capture the current + // screen state if necessary + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOutsidePaintEvent, true); // for bitBlt in resume() + + setCursor( Qt::BlankCursor ); + + kDebug(1204) << "Saver window Id: " << winId(); +} + +//--------------------------------------------------------------------------- +// +// Hide the screensaver window +// +void LockProcess::hideSaverWindow() +{ + hide(); + lower(); + removeVRoot(winId()); + XDeleteProperty(QX11Info::display(), winId(), gXA_SCREENSAVER_VERSION); + if ( gVRoot ) { + unsigned long vroot_data[1] = { gVRootData }; + XChangeProperty(QX11Info::display(), gVRoot, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); + gVRoot = 0; + } + XSync(QX11Info::display(), False); +} + +//--------------------------------------------------------------------------- +static int ignoreXError(Display *, XErrorEvent *) +{ + return 0; +} + +//--------------------------------------------------------------------------- +// +// Save the current virtual root window +// +void LockProcess::saveVRoot() +{ + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + QX11Info info; + Window root = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen())); + + gVRoot = 0; + gVRootData = 0; + + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + + if (XQueryTree(QX11Info::display(), root, &rootReturn, &parentReturn, + &children, &numChildren)) + { + for (unsigned int i = 0; i < numChildren; i++) + { + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned char *newRoot = 0; + + if ((XGetWindowProperty(QX11Info::display(), children[i], gXA_VROOT, 0, 1, + False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter, + &newRoot) == Success) && newRoot) + { + gVRoot = children[i]; + Window *dummy = (Window*)newRoot; + gVRootData = *dummy; + XFree ((char*) newRoot); + break; + } + } + if (children) + { + XFree((char *)children); + } + } + + XSetErrorHandler(oldHandler); +} + +//--------------------------------------------------------------------------- +// +// Set the virtual root property +// +void LockProcess::setVRoot(Window win, Window vr) +{ + if (gVRoot) + removeVRoot(gVRoot); + + QX11Info info; + unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen())); + unsigned long vroot_data[1] = { vr }; + + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Window top = win; + while (1) { + if (!XQueryTree(QX11Info::display(), top , &rootReturn, &parentReturn, + &children, &numChildren)) + return; + if (children) + XFree((char *)children); + if (parentReturn == rw) { + break; + } else + top = parentReturn; + } + + XChangeProperty(QX11Info::display(), top, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); +} + +//--------------------------------------------------------------------------- +// +// Remove the virtual root property +// +void LockProcess::removeVRoot(Window win) +{ + XDeleteProperty (QX11Info::display(), win, gXA_VROOT); +} + +//--------------------------------------------------------------------------- +// +// Grab the keyboard. Returns true on success +// +bool LockProcess::grabKeyboard() +{ + int rv = XGrabKeyboard( QX11Info::display(), QApplication::desktop()->winId(), + True, GrabModeAsync, GrabModeAsync, CurrentTime ); + + return (rv == GrabSuccess); +} + +#define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask + +//--------------------------------------------------------------------------- +// +// Grab the mouse. Returns true on success +// +bool LockProcess::grabMouse() +{ + int rv = XGrabPointer( QX11Info::display(), QApplication::desktop()->winId(), + True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, + QCursor(Qt::BlankCursor).handle(), CurrentTime ); + + return (rv == GrabSuccess); +} + +//--------------------------------------------------------------------------- +// +// Grab keyboard and mouse. Returns true on success. +// +bool LockProcess::grabInput() +{ + XSync(QX11Info::display(), False); + + if (!grabKeyboard()) + { + sleep(1); + if (!grabKeyboard()) + { + return false; + } + } + + if (!grabMouse()) + { + sleep(1); + if (!grabMouse()) + { + XUngrabKeyboard(QX11Info::display(), CurrentTime); + return false; + } + } + + lockXF86(); + + return true; +} + +//--------------------------------------------------------------------------- +// +// Release mouse an keyboard grab. +// +void LockProcess::ungrabInput() +{ + XUngrabKeyboard(QX11Info::display(), CurrentTime); + XUngrabPointer(QX11Info::display(), CurrentTime); + unlockXF86(); +} + +//--------------------------------------------------------------------------- +// +// Start the screen saver. +// +bool LockProcess::startSaver() +{ + if (!child_saver && !grabInput()) + { + kWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" ; + return false; + } + mBusy = false; + + saveVRoot(); + + if (mParent) { + QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this); + connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver())); + } + if (mAutoLogoutTimeout && !mSetupMode) + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds + createSaverWindow(); + move(0, 0); + show(); + setCursor( Qt::BlankCursor ); + + raise(); + XSync(QX11Info::display(), False); + + setVRoot( winId(), winId() ); + startHack(); + startPlasma(); + KNotification::event( QLatin1String( "savingstarted" ) ); + return true; +} + +//--------------------------------------------------------------------------- +// +// Stop the screen saver. +// +void LockProcess::stopSaver() +{ + kDebug(1204) << "LockProcess: stopping saver"; + resume( true ); + stopPlasma(); + stopHack(); + hideSaverWindow(); + mVisibility = false; + if (!child_saver) { + if (mLocked) { + KDisplayManager().setLock( false ); + mLocked = false; + KNotification *u = new KNotification( QLatin1String( "unlocked" ) ); + u->sendEvent(); + } + ungrabInput(); + const char *out = "GOAWAY!"; + for (QList::ConstIterator it = child_sockets.constBegin(); it != child_sockets.constEnd(); ++it) + write(*it, out, sizeof(out)); + } + KNotification *s = new KNotification( QLatin1String( "savingstopped" ) ); + s->sendEvent(); +} + +// private static +QVariant LockProcess::getConf(void *ctx, const char *key, const QVariant &dflt) +{ + LockProcess *that = (LockProcess *)ctx; + QString fkey = QLatin1String( key ) % QLatin1Char( '=' ); + for (QStringList::ConstIterator it = that->mPluginOptions.constBegin(); + it != that->mPluginOptions.constEnd(); ++it) + if ((*it).startsWith( fkey )) + return (*it).mid( fkey.length() ); + return dflt; +} + +void LockProcess::cantLock( const QString &txt) +{ + msgBox( 0, QMessageBox::Critical, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt ); +} + +#if 0 // placeholders for later +i18n("Cannot start kcheckpass."); +i18n("kcheckpass is unable to operate. Possibly it is not setuid root."); +#endif + +//--------------------------------------------------------------------------- +// +// Make the screen saver password protected. +// +bool LockProcess::startLock() +{ + if (loadGreetPlugin()) { + mLocked = true; + KDisplayManager().setLock(true); + lockPlasma(); + KNotification::event( QLatin1String( "locked" ) ); + return true; + } + return false; +} + +bool LockProcess::loadGreetPlugin() +{ + if (greetPlugin.library) { + //we were locked once before, so all the plugin loading's done already + //FIXME should I be unloading the plugin on unlock instead? + return true; + } + for (QStringList::ConstIterator it = mPlugins.constBegin(); it != mPlugins.constEnd(); ++it) { + GreeterPluginHandle plugin; + KLibrary *lib = new KLibrary( (*it)[0] == QLatin1Char( '/' ) ? *it : QLatin1String( "kgreet_" ) + *it ); + if (lib->fileName().isEmpty()) { + kWarning(1204) << "GreeterPlugin " << *it << " does not exist" ; + delete lib; + continue; + } + if (!lib->load()) { + kWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << lib->fileName() << ")" ; + delete lib; + continue; + } + plugin.library = lib; + plugin.info = (KGreeterPluginInfo *)lib->resolveSymbol( "kgreeterplugin_info" ); + if (!plugin.info ) { + kWarning(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") is no valid greet widget plugin" ; + lib->unload(); + delete lib; + continue; + } + if (plugin.info->method && !mMethod.isEmpty() && mMethod != QLatin1String( plugin.info->method )) { + kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") serves " << plugin.info->method << ", not " << mMethod; + lib->unload(); + delete lib; + continue; + } + if (!plugin.info->init( mMethod, getConf, this )) { + kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") refuses to serve " << mMethod; + lib->unload(); + delete lib; + continue; + } + kDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded"; + greetPlugin = plugin; + return true; + } + cantLock( i18n("No appropriate greeter plugin configured.") ); + return false; +} + +//--------------------------------------------------------------------------- +// + + +bool LockProcess::startHack() +{ + kDebug(1204) << "Starting hack:" << mSaverExec; + + if (mSaverExec.isEmpty() || mForbidden) + { + hackExited(); + return false; + } + + QHash keyMap; + keyMap.insert(QLatin1Char( 'w' ), QString::number(winId())); + mHackProc << KShell::splitArgs(KMacroExpander::expandMacrosShellQuote(mSaverExec, keyMap)); + + mHackProc.start(); + if (mHackProc.waitForStarted()) + { +#ifdef HAVE_SETPRIORITY + setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority); +#endif + return true; + } + + hackExited(); + return false; +} + +//--------------------------------------------------------------------------- +// +void LockProcess::stopHack() +{ + if (mHackProc.state() != QProcess::NotRunning) + { + mHackProc.terminate(); + if (!mHackProc.waitForFinished(10000)) + { + mHackProc.kill(); + } + } +} + +//--------------------------------------------------------------------------- +// +void LockProcess::hackExited() +{ + // Hack exited while we're supposed to be saving the screen. + // Make sure the saver window is black. + XSetWindowBackground(QX11Info::display(), winId(), BlackPixel( QX11Info::display(), QX11Info::appScreen())); + XClearWindow(QX11Info::display(), winId()); +} + +bool LockProcess::startPlasma() +{ + if (!mPlasmaEnabled) { + return false; + } + + if (mSetupMode) { + mSuppressUnlock.start(mSuppressUnlockTimeout); + XChangeActivePointerGrab(QX11Info::display(), GRABEVENTS, + QCursor(Qt::ArrowCursor).handle(), CurrentTime); + } + + kDebug() << "looking for plasma-overlay"; + if (!mPlasmaDBus) { + //try to get it, in case it's already running somehow + //mPlasmaDBus = new QDBusInterface(s_overlayServiceName, "/MainApplication", QString(), + mPlasmaDBus = new org::kde::plasmaoverlay::App(s_overlayServiceName, QLatin1String( "/App" ), + QDBusConnection::sessionBus(), this); + //FIXME this might-already-be-running stuff seems really really Wrong. + } + + if (mPlasmaDBus->isValid()) { + kDebug() << "weird, plasma-overlay is already running"; + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "setup" ), mSetupMode); + return true; + } + + kDebug () << "...not found" << "starting plasma-overlay"; + delete mPlasmaDBus; + mPlasmaDBus = 0; + + if (!mServiceWatcher) { + mServiceWatcher = new QDBusServiceWatcher(s_overlayServiceName, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForOwnerChange, this); + connect(mServiceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(newService(QString,QString,QString))); + } + + KProcess *plasmaProc = new KProcess; + plasmaProc->setProgram(QLatin1String( "plasma-overlay" )); + if (mSetupMode) { + *plasmaProc << QLatin1String( "--setup" ); + } + + //make sure it goes away when it's done (and not before) + connect(plasmaProc, SIGNAL(finished(int,QProcess::ExitStatus)), plasmaProc, SLOT(deleteLater())); + + plasmaProc->start(); + kDebug() << "process begun"; + + //plasma gets 15 seconds to load, or we assume it failed + QTimer::singleShot(15 * 1000, this, SLOT(checkPlasma())); + return true; +} + +void LockProcess::checkPlasma() +{ + if (!mPlasmaEnabled) { + kDebug() << "You're Doing It Wrong!"; + return; + } + if (mPlasmaDBus && mPlasmaDBus->isValid()) { + //hooray, looks like it started ok + kDebug() << "success!"; + //...but just in case, make sure we're not waiting on it + mSetupMode = false; + return; + } + + kDebug() << "ohnoes. plasma = teh fail."; + disablePlasma(); +} + +bool LockProcess::isPlasmaValid() +{ + //FIXME I'm assuming that if it's valid, calls will succeed. so if that's not the case we'll + //need to change things so that plasma's disabled properly if it fails + //damn. isValid is not quite enough. a call may still fail, and then we need to bail. + if (!(mPlasmaEnabled && mPlasmaDBus)) { + return false; //no plasma, at least not yet + } + if (mPlasmaDBus->isValid()) { + return true; + } + //oh crap, it ran away on us. + disablePlasma(); + return false; +} + +void LockProcess::disablePlasma() +{ + kDebug(); + mPlasmaEnabled = false; + mSetupMode = false; + mSuppressUnlock.stop(); //FIXME we might need to start the lock timer ala deactivatePlasma() + //actually we could be lazy and just call deactivatePlasma() TODO check that this'll really work + delete mPlasmaDBus; + mPlasmaDBus=0; +} + +void LockProcess::stopPlasma() +{ + if (mPlasmaDBus && mPlasmaDBus->isValid()) { + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "quit" )); + } else { + kDebug() << "cannot stop plasma-overlay"; + } +} + +void LockProcess::newService(QString name, QString oldOwner, QString newOwner) +{ + Q_UNUSED(name); + Q_UNUSED(oldOwner); + + if (mPlasmaDBus) { + if (newOwner.isEmpty()) { + kDebug() << "plasma ran away?"; + disablePlasma(); + } else { + kDebug() << "I'm confused!!"; + } + return; + } + + kDebug() << "plasma! yaay!"; + mPlasmaDBus = new org::kde::plasmaoverlay::App(s_overlayServiceName, QLatin1String( "/App" ), QDBusConnection::sessionBus(), this); + + //XXX this isn't actually used any more iirc + connect(mPlasmaDBus, SIGNAL(hidden()), SLOT(unSuppressUnlock())); + + if (!mDialogs.isEmpty()) { + //whoops, activation probably failed earlier + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "setActive" ), true); + } +} + +void LockProcess::deactivatePlasma() +{ + if (isPlasmaValid()) { + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "setActive" ), false); + } + if (!mLocked && mLockGrace >=0) { + QTimer::singleShot(mLockGrace, this, SLOT(startLock())); //this is only ok because any activity will quit + } +} + +void LockProcess::lockPlasma() +{ + if (isPlasmaValid()) { + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "lock" )); + } +} + +void LockProcess::unSuppressUnlock() +{ + //note: suppressing unlock also now means suppressing quit-on-activity + //maybe some var renaming is in order. + mSuppressUnlock.stop(); +} + +void LockProcess::quit() +{ + mSuppressUnlock.stop(); + if (!mLocked || checkPass()) { + quitSaver(); + } +} + +void LockProcess::suspend() +{ + if( !mSuspended && mHackProc.state() == QProcess::Running ) + { + ::kill(mHackProc.pid(), SIGSTOP); + // We actually want to wait for the stopped hack's X commands + // having been handled, but that would require a custom + // protocol which would cause the hack to call XSync() and + // freeze itself. So just go to sleep and hope that the X + // server will have enough time ... + usleep(100000); + mSavedScreen = QPixmap::grabWindow( winId()); + mSnapshotTimer.setSingleShot(true); + mSnapshotTimer.start(1000); + } + mSuspended = true; +} + +void LockProcess::resume( bool force ) +{ + if( !force && (!mDialogs.isEmpty() || !mVisibility )) + return; // no resuming with dialog visible or when not visible + if( mSuspended && mHackProc.state() == QProcess::Running ) + { + QPainter p( this ); + if (!mSavedScreen.isNull()) + p.drawPixmap( 0, 0, mSavedScreen ); + else + p.fillRect( rect(), Qt::black ); + p.end(); + QApplication::syncX(); + mSavedScreen = QPixmap(); + ::kill(mHackProc.pid(), SIGCONT); + } + mSuspended = false; +} + +//--------------------------------------------------------------------------- +// +// Show the password dialog +// This is called only in the master process +// +bool LockProcess::checkPass() +{ + if (isPlasmaValid()) { + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "setActive" ), true); + } + + PasswordDlg passDlg( this, &greetPlugin); + const int ret = execDialog( &passDlg ); + + if (isPlasmaValid()) { + if (ret == QDialog::Rejected) { + mSuppressUnlock.start(mSuppressUnlockTimeout); + } else if (ret == TIMEOUT_CODE) { + mPlasmaDBus->call(QDBus::NoBlock, QLatin1String( "setActive" ), false); + } + } + + XWindowAttributes rootAttr; + XGetWindowAttributes(QX11Info::display(), QX11Info::appRootWindow(), &rootAttr); + if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 ) + { + kWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" ; + XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), + SubstructureNotifyMask | rootAttr.your_event_mask ); + } + + return ret == QDialog::Accepted; +} + +bool LockProcess::checkPass(const QString &reason) +{ + if (! mLocked) { + //we were never locked... how can we unlock?! + //if anyone finds a use case for checking the password while unlocked, they'll have to load + //the greetplugin n'stuff + return false; + } + PasswordDlg passDlg(this, &greetPlugin, reason); + const int ret = execDialog( &passDlg ); +// kDebug() << ret; + + //FIXME do we need to copy&paste that SubstructureNotifyMask code above? + if (ret == QDialog::Accepted) { + //we don't quit on a custom checkpass, but we do unlock + //so that the user doesn't have to type their password twice + mLocked = false; + KDisplayManager().setLock(false); + KNotification::event( QLatin1String( "unlocked" ) ); + //FIXME while suppressUnlock *should* always be running, if it isn't + //(say if someone's doing things they shouldn't with dbus) then it won't get started by this + //which means that a successful unlock will never re-lock + //in fact, the next bit of activity would lead to the screensaver quitting. + //possible solutions: + //-treat this function like activity: quit if already unlocked, ensure suppress is started + //if we're locked and the dialog's rejected + //-return true if already unlocked, without doing anything, same as above if locked + //-let it quit, and tell people not to do such silly things :P + return true; + } + return false; +} + +static void fakeFocusIn( WId window ) +{ + // We have keyboard grab, so this application will + // get keyboard events even without having focus. + // Fake FocusIn to make Qt realize it has the active + // window, so that it will correctly show cursor in the dialog. + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xfocus.display = QX11Info::display(); + ev.xfocus.type = FocusIn; + ev.xfocus.window = window; + ev.xfocus.mode = NotifyNormal; + ev.xfocus.detail = NotifyAncestor; + XSendEvent( QX11Info::display(), window, False, NoEventMask, &ev ); +} + +bool LockProcess::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Resize) { + QWidget *w = static_cast(o); + mFrames.value(w)->resize(w->size()); + } + return false; +} + +int LockProcess::execDialog( QDialog *dlg ) +{ + + QFrame *winFrame = new QFrame( dlg ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + winFrame->lower(); + mFrames.insert(dlg, winFrame); + dlg->installEventFilter(this); + + dlg->adjustSize(); + + int screen = Kephal::ScreenUtils::primaryScreenId(); + if (Kephal::ScreenUtils::numScreens() > 1) { + screen = Kephal::ScreenUtils::screenId(QCursor::pos()); + } + + const QRect screenRect = Kephal::ScreenUtils::screenGeometry(screen); + QRect rect = dlg->geometry(); + rect.moveCenter(screenRect.center()); + dlg->move(rect.topLeft()); + + if (mDialogs.isEmpty()) + { + if (mAutoLogoutTimerId) + killTimer(mAutoLogoutTimerId); + suspend(); + XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS, + QCursor(Qt::ArrowCursor).handle(), CurrentTime); + } + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId()); + const int rt = dlg->exec(); + const int pos = mDialogs.indexOf( dlg ); + if (pos != -1) + mDialogs.remove( pos ); + if( mDialogs.isEmpty() ) { + resume( false ); + if (mAutoLogoutTimerId) + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); + } + updateFocus(); + + dlg->removeEventFilter(this); + mFrames.remove(dlg); + + return rt; +} + +void LockProcess::updateFocus() +{ + if (mDialogs.isEmpty()) { + if (mForeignInputWindows.isEmpty()) { + XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS, + QCursor(Qt::BlankCursor).handle(), CurrentTime); + } else { + fakeFocusIn(mForeignInputWindows.first()); + } + } else { + fakeFocusIn(mDialogs.first()->winId()); + } +} + +//--------------------------------------------------------------------------- +// +// X11 Event. +// +bool LockProcess::x11Event(XEvent *event) +{ + if (mEventRecursed) + return false; + + bool ret = false; + switch (event->type) + { + case ButtonPress: + if (!mDialogs.isEmpty() && event->xbutton.window == event->xbutton.root) { + //kDebug() << "close" << mDialogs.first()->effectiveWinId(); + KDialog *dlg = qobject_cast(mDialogs.first()); + if (dlg) { + //kDebug() << "casting success"; + dlg->reject(); + } + break; + } + case KeyPress: + case MotionNotify: + if (mBusy || !mDialogs.isEmpty()) { + //kDebug() << "busy"; + //FIXME shouldn't we be resetting some timers? + break; + } + mBusy = true; + //something happened. do we quit, ask for a password or forward it to plasma? + //if we're supposed to be forwarding, we check that there's actually a plasma window up + //so that the user isn't trapped if plasma crashes or is slow to load. + //however, if plasma started in setup mode, we don't want to let anything happen until + //it has a chance to load. + //note: mSetupMode should end when we either get a winid or hit the checkPlasma timeout + if (mSuppressUnlock.isActive() && (mSetupMode || !mForeignInputWindows.isEmpty())) { + mSuppressUnlock.start(); //help, help, I'm being suppressed! + if (mAutoLogoutTimerId) { + killTimer(mAutoLogoutTimerId); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); + } + } else if (!mLocked) { + quitSaver(); + mBusy = false; + return true; //it's better not to forward any input while quitting, right? + } else { + if (event->type == KeyPress) { + // Bounce the keypress to the dialog + QByteArray chars; + chars.resize(513); + KeySym keysym; + XLookupString(&event->xkey, chars.data(), chars.size(), &keysym, 0); + switch (keysym) { + // These would cause immediate failure + case XK_Escape: + case XK_Return: + case XK_KP_Enter: + // These just make no sense + case XK_Tab: + case XK_space: + break; + default: + mEventQueue.enqueue(*event); + } + } + if (checkPass()) { + quitSaver(); + mBusy = false; + return true; //it's better not to forward any input while quitting, right? + } + } + mBusy = false; + ret = true; + break; + + case VisibilityNotify: + if( event->xvisibility.window == winId()) + { // mVisibility == false means the screensaver is not visible at all + // e.g. when switched to text console + // ...or when plasma's over it non-compositely? + // hey, this gives me free "suspend saver when plasma obscures it" + mVisibility = !(event->xvisibility.state == VisibilityFullyObscured); + if (!mVisibility) { + mSuspendTimer.start(2000); + kDebug(1204) << "fully obscured"; + } else { + kDebug(1204) << "not fully obscured"; + mSuspendTimer.stop(); + resume( false ); + } + if (mForeignWindows.isEmpty() && event->xvisibility.state != VisibilityUnobscured) { + kDebug(1204) << "no plasma; saver obscured"; + stayOnTop(); + } + } else if (!mForeignWindows.isEmpty() && event->xvisibility.window == mForeignWindows.last() && + event->xvisibility.state != VisibilityUnobscured) { + //FIXME now that we have several plasma winids this doesn't feel valid + //but I don't know what to do about it! + kDebug(1204) << "plasma obscured!"; + stayOnTop(); + } + break; + + case ConfigureNotify: // from SubstructureNotifyMask on the root window + if(event->xconfigure.event == QX11Info::appRootWindow()) { + int index = findWindowInfo( event->xconfigure.window ); + if( index >= 0 ) { + int index2 = event->xconfigure.above ? findWindowInfo( event->xconfigure.above ) : 0; + if( index2 < 0 ) + kDebug(1204) << "Unknown above for ConfigureNotify"; + else { // move just above the other window + if( index2 < index ) + ++index2; + windowInfo.move( index, index2 ); + } + } else + kDebug(1204) << "Unknown toplevel for ConfigureNotify"; + //kDebug() << "ConfigureNotify:"; + //the stacking order changed, so let's change the stacking order again to what we want + stayOnTop(); + } + break; + case MapNotify: // from SubstructureNotifyMask on the root window + if( event->xmap.event == QX11Info::appRootWindow()) { + kDebug(1204) << "MapNotify:" << event->xmap.window; + if (!mDialogs.isEmpty() && mDialogs.first()->winId() == event->xmap.window) + mVisibleDialogs.append(event->xmap.window); + int index = findWindowInfo( event->xmap.window ); + if( index >= 0 ) + windowInfo[ index ].viewable = true; + else + kDebug(1204) << "Unknown toplevel for MapNotify"; + KXErrorHandler err; // ignore X errors here + WindowType type = windowType(event->xmap.window); + if (type != IgnoreWindow) { + if (mForeignWindows.contains(event->xmap.window)) { + kDebug(1204) << "uhoh! duplicate!"; + } else { + //ordered youngest-on-top + mForeignWindows.prepend(event->xmap.window); + } + if (type & InputWindow) { + kDebug(1204) << "input window"; + if (mForeignInputWindows.contains(event->xmap.window)) { + kDebug(1204) << "uhoh! duplicate again"; //never happens + } else { + //ordered youngest-on-top + mForeignInputWindows.prepend(event->xmap.window); + fakeFocusIn(event->xmap.window); + } + mSetupMode = false; //no more waiting for plasma + } + } + stayOnTop(); + } + break; + case UnmapNotify: + if (event->xunmap.event == QX11Info::appRootWindow()) { + kDebug(1204) << "UnmapNotify:" << event->xunmap.window; + int index = findWindowInfo( event->xunmap.window ); + if( index >= 0 ) + windowInfo[ index ].viewable = false; + else + kDebug(1204) << "Unknown toplevel for MapNotify"; + mVisibleDialogs.removeAll(event->xunmap.window); + mForeignWindows.removeAll(event->xunmap.window); + if (mForeignInputWindows.removeAll(event->xunmap.window)) { + updateFocus(); + } + } + break; + case CreateNotify: + if (event->xcreatewindow.parent == QX11Info::appRootWindow()) { + kDebug(1204) << "CreateNotify:" << event->xcreatewindow.window; + int index = findWindowInfo( event->xcreatewindow.window ); + if( index >= 0 ) + kDebug(1204) << "Already existing toplevel for CreateNotify"; + else { + WindowInfo info; + info.window = event->xcreatewindow.window; + info.viewable = false; + windowInfo.append( info ); + } + } + break; + case DestroyNotify: + if (event->xdestroywindow.event == QX11Info::appRootWindow()) { + int index = findWindowInfo( event->xdestroywindow.window ); + if( index >= 0 ) + windowInfo.removeAt( index ); + else + kDebug(1204) << "Unknown toplevel for DestroyNotify"; + } + break; + case ReparentNotify: + if (event->xreparent.event == QX11Info::appRootWindow() && event->xreparent.parent != QX11Info::appRootWindow()) { + int index = findWindowInfo( event->xreparent.window ); + if( index >= 0 ) + windowInfo.removeAt( index ); + else + kDebug(1204) << "Unknown toplevel for ReparentNotify away"; + } else if (event->xreparent.parent == QX11Info::appRootWindow()) { + int index = findWindowInfo( event->xreparent.window ); + if( index >= 0 ) + kDebug(1204) << "Already existing toplevel for ReparentNotify"; + else { + WindowInfo info; + info.window = event->xreparent.window; + info.viewable = false; + windowInfo.append( info ); + } + } + break; + case CirculateNotify: + if (event->xcirculate.event == QX11Info::appRootWindow()) { + int index = findWindowInfo( event->xcirculate.window ); + if( index >= 0 ) { + windowInfo.move( index, event->xcirculate.place == PlaceOnTop ? windowInfo.size() - 1 : 0 ); + } else + kDebug(1204) << "Unknown toplevel for CirculateNotify"; + } + break; + } + + // We have grab with the grab window being the root window. + // This results in key events being sent to the root window, + // but they should be sent to the dialog if it's visible. + // It could be solved by setFocus() call, but that would mess + // the focus after this process exits. + // Qt seems to be quite hard to persuade to redirect the event, + // so let's simply dupe it with correct destination window, + // and ignore the original one. + if (!mDialogs.isEmpty()) { + if (event->type == KeyPress || event->type == KeyRelease) { + mEventQueue.enqueue(*event); + ret = true; + } + if (!mVisibleDialogs.isEmpty()) + while (!mEventQueue.isEmpty()) { + //kDebug() << "forward to dialog"; + XEvent ev2 = mEventQueue.dequeue(); + ev2.xkey.window = ev2.xkey.subwindow = mVisibleDialogs.last(); + mEventRecursed = true; + qApp->x11ProcessEvent( &ev2 ); + mEventRecursed = false; + } + } else { + mEventQueue.clear(); + if (!mForeignInputWindows.isEmpty()) { + //when there are no dialogs, forward some events to plasma + switch (event->type) { + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: + case MotionNotify: { + //kDebug() << "forward to plasma"; + XEvent ev2 = *event; + Window root_return; + int x_return, y_return; + unsigned int width_return, height_return, border_width_return, depth_return; + WId targetWindow = 0; + //kDebug() << "root is" << winId(); + //kDebug() << "search window under pointer with" << mForeignInputWindows.size() << "windows"; + KXErrorHandler err; // ignore X errors + foreach(WId window, mForeignInputWindows) + { + if( XGetGeometry(QX11Info::display(), window, &root_return, + &x_return, &y_return, + &width_return, &height_return, + &border_width_return, &depth_return) + && + (event->xkey.x>=x_return && event->xkey.x<=x_return+(int)width_return) + && + (event->xkey.y>=y_return && event->xkey.y<=y_return+(int)height_return) ) + { + //kDebug() << "found window" << window; + targetWindow = window; + ev2.xkey.window = ev2.xkey.subwindow = targetWindow; + ev2.xkey.x = event->xkey.x - x_return; + ev2.xkey.y = event->xkey.y - y_return; + break; + } + } + XSendEvent(QX11Info::display(), targetWindow, False, NoEventMask, &ev2); + ret = true; + break; } + default: + break; + } + } + } + + return ret; +} + +LockProcess::WindowType LockProcess::windowType(WId id) +{ + Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False); + Atom actualType; + int actualFormat; + unsigned long nitems, remaining; + unsigned char *data = 0; + Display *display = QX11Info::display(); + + int result = XGetWindowProperty(display, id, tag, 0, 1, False, tag, &actualType, + &actualFormat, &nitems, &remaining, &data); + + //kDebug() << (result == Success) << (actualType == tag); + WindowType type = IgnoreWindow; + if (result == Success && actualType == tag) { + if (nitems != 1 || actualFormat != 8) { + kDebug(1204) << "malformed property"; + } else { + kDebug(1204) << "i can haz plasma window?" << data[0]; + switch (data[0]) { + case 0: //FIXME magic numbers + type = SimpleWindow; + break; + case 1: + type = InputWindow; + break; + case 2: + type = DefaultWindow; + break; + } + } + } + if (data) { + XFree(data); + } + return type; +} + +void LockProcess::stayOnTop() +{ + // this restacking is written in a way so that + // if the stacking positions actually don't change, + // all restacking operations will be no-op, + // and no ConfigureNotify will be generated, + // thus avoiding possible infinite loops + QVector< Window > stack( mDialogs.count() + mForeignWindows.count() + 1 ); + int count = 0; + // dialogs first + foreach( QWidget* w, mDialogs ) + stack[ count++ ] = w->winId(); + // now the plasma stuff below the dialogs + foreach( WId w, mForeignWindows ) + stack[ count++ ] = w; + // finally, the saver window + stack[ count++ ] = winId(); + // We actually have to check the current stacking order. When an override-redirect + // window is shown or raised, it can get above the screensaver window and there's not + // much to do to prevent it (only the compositing manager can prevent that). This + // is detected by the screenlocker and handled here, but the contents of the window + // may remain visible, since some screensavers don't react to Expose events and + // don't repaint as necessary. Therefore, if a window is detected above any of the windows + // related to screenlocking, I don't see any better possibility than to completely + // erase the screenlocker window. + // It is important to first detect, then restack and then erase. + // Another catch here is that only viewable windows matter, but checking here whether + // a window is viewable is a race condition, since a window may map, paint and unmap + // before we reach this point, thus making this code fail to detect the need to do + // a repaint. Therefore we track all relevant X events about mapping state of toplevel + // windows (which ensures proper ordering) and here just consult the information. + bool needs_erase = false; + bool found_ours = false; + foreach( const WindowInfo& info, windowInfo ) { + if( stack.contains( info.window )) { + found_ours = true; + } else if( found_ours && info.viewable ) { + kDebug(1204) << "found foreign window above screensaver"; + needs_erase = true; + break; + } + } + // do the actual restacking if needed + XRaiseWindow( x11Info().display(), stack[ 0 ] ); + if( count > 1 ) + XRestackWindows( x11Info().display(), stack.data(), count ); + if( needs_erase ) { + // if the snapshot was taken recently it is possible that the rogue + // window was snapshotted at well. + if (mSnapshotTimer.isActive()) + mSavedScreen = QPixmap(); + QPainter p( this ); + if (!mSavedScreen.isNull()) + p.drawPixmap( 0, 0, mSavedScreen ); + else + p.fillRect( rect(), Qt::black ); + p.end(); + QApplication::syncX(); + } +} + +void LockProcess::checkDPMSActive() +{ +#ifdef HAVE_DPMS + BOOL on; + CARD16 state; + DPMSInfo(QX11Info::display(), &state, &on); + //kDebug() << "checkDPMSActive " << on << " " << state; + if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff) + suspend(); + else + resume( false ); +#endif +} + +#if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE) +// see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113 +// This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config, +// and kscreenlocker will still lock the session. +static enum { Unknown, Yes, No } can_do_xf86_lock = Unknown; +void LockProcess::lockXF86() +{ + if( can_do_xf86_lock == Unknown ) + { + int major, minor, dummy; + if( XF86MiscQueryExtension( QX11Info::display(), &dummy, &dummy ) + && XF86MiscQueryVersion( QX11Info::display(), &major, &minor ) + && (major > 0 || minor >= 5) ) + can_do_xf86_lock = Yes; + else + can_do_xf86_lock = No; + } + if( can_do_xf86_lock != Yes ) + return; + if( mRestoreXF86Lock ) + return; + if( XF86MiscSetGrabKeysState( QX11Info::display(), False ) != MiscExtGrabStateSuccess ) + return; + // success + mRestoreXF86Lock = true; +} + +void LockProcess::unlockXF86() +{ + if( can_do_xf86_lock != Yes ) + return; + if( !mRestoreXF86Lock ) + return; + XF86MiscSetGrabKeysState( QX11Info::display(), True ); + mRestoreXF86Lock = false; +} +#else +void LockProcess::lockXF86() +{ +} + +void LockProcess::unlockXF86() +{ +} +#endif + +void LockProcess::msgBox( QWidget *parent, QMessageBox::Icon type, const QString &txt ) +{ + QDialog box( parent, Qt::X11BypassWindowManagerHint ); + + QLabel *label1 = new QLabel( &box ); + label1->setPixmap( QMessageBox::standardIcon( type ) ); + QLabel *label2 = new QLabel( txt, &box ); + KPushButton *button = new KPushButton( KStandardGuiItem::ok(), &box ); + button->setDefault( true ); + button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + connect( button, SIGNAL( clicked() ), &box, SLOT( accept() ) ); + + QGridLayout *grid = new QGridLayout( &box ); + grid->setSpacing( 10 ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + grid->addWidget( button, 1, 0, 1, 2, Qt::AlignCenter ); + + execDialog( &box ); +} + +int LockProcess::findWindowInfo( Window w ) +{ + for( int i = 0; + i < windowInfo.size(); + ++i ) + if( windowInfo[ i ].window == w ) + return i; + return -1; +} + +#include "lockprocess.moc" diff --git a/kwin/screenlocker/lock/lockprocess.h b/kwin/screenlocker/lock/lockprocess.h new file mode 100644 index 0000000..8b6d9a8 --- /dev/null +++ b/kwin/screenlocker/lock/lockprocess.h @@ -0,0 +1,238 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Oswald Buddenhagen +// Copyright 2008 Chani Armitage +// + +#ifndef LOCKPROCESS_H +#define LOCKPROCESS_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "plasmaapp_interface.h" + +class KLibrary; + +struct KGreeterPluginInfo; + +struct GreeterPluginHandle { + KLibrary *library; + KGreeterPluginInfo *info; +}; + +const int TIMEOUT_CODE = 2; //from PasswordDlg + +class QDBusServiceWatcher; + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry. +// +class LockProcess + : public QWidget +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.screenlocker.LockProcess") +public: + explicit LockProcess(bool child_saver = false, bool useBlankOnly = false); + ~LockProcess(); + + /** + * start the screensaver locked + */ + bool lock(bool initial = false); + + /** + * start the screensaver unlocked + */ + bool defaultSave(); + + /** + * start the screensaver in plasma setup mode + * if plasma is disabled this just acts like defaultSave + */ + bool startSetup(); + + /** + * start the screensaver unlocked, and *never* automatically lock it + */ + bool dontLock(); + + void setChildren(QList children) { child_sockets = children; } + void setParent(int fd) { mParent = fd; } + + void msgBox( QWidget *parent, QMessageBox::Icon type, const QString &txt ); + int execDialog( QDialog* dlg ); + +public Q_SLOTS: + void quitSaver(); + //dbus methods + /** + * bring up the password dialog with @param reason displayed instead of the usual "this session + * is locked" message. + * @return true if the password was entered correctly + * if this returns true, it will also unlock the screensaver without quitting. + * it will re-lock after the lock timeout in the settings + */ + Q_SCRIPTABLE bool checkPass(const QString &reason); + /** + * this will unlock and quit the screensaver, asking for a password first if necessary + */ + Q_SCRIPTABLE void quit(); + /** + * immediately lock the screen; it will now require a password to unlock. + */ + Q_SCRIPTABLE bool startLock(); + +protected: + virtual bool x11Event(XEvent *); + virtual void timerEvent(QTimerEvent *); + virtual bool eventFilter(QObject *o, QEvent *e); + +private Q_SLOTS: + void hackExited(); + void signalPipeSignal(); + void suspend(); + void checkDPMSActive(); + void slotDeadTimePassed(); + /** + * check that plasma started properly (used for timeout) + * and disable it if it failed + */ + void checkPlasma(); + /** + * a new dbus service has come in + */ + void newService(QString name, QString oldOwner, QString newOwner); + /** + * tell plasma we're in idle mode + */ + void deactivatePlasma(); + void lockPlasma(); + /** + * immediately un-suppress the password dialog + * FIXME need a better name + */ + void unSuppressUnlock(); + +private: + void configure(); + void readSaver(); + void createSaverWindow(); + void hideSaverWindow(); + void saveVRoot(); + void setVRoot(Window win, Window rw); + void removeVRoot(Window win); + bool grabKeyboard(); + bool grabMouse(); + bool grabInput(); + void ungrabInput(); + void cantLock(const QString &reason); + bool startSaver(); + void stopSaver(); + bool startHack(); + void stopHack(); + bool startPlasma(); + void stopPlasma(); + void setupSignals(); + /** + * exec the password dialog + * @return true iff the password was checked and is valid + */ + bool checkPass(); + /** + * returns true if plasma is up and the dbus interface is valid + */ + bool isPlasmaValid(); + /** + * give up on plasma, probably because it crashed. + * this does *not* tell plasma to quit. it just stops using it. + */ + void disablePlasma(); + /** + * give a fakefocusin to the right window + */ + void updateFocus(); + void stayOnTop(); + int findWindowInfo( Window window ); // returns index in windowInfo or -1 + void lockXF86(); + void unlockXF86(); + void resume( bool force ); + enum WindowType { IgnoreWindow = 0 /** regular window to be left below the saver */, + SimpleWindow = 1 /** simple popup that can't handle direct input */, + InputWindow = 2 /** annoying dialog that needs direct input */, + DefaultWindow = 6/** input window that's also the plasma view */ + }; + /** + * @return the type of window, based on its X property + */ + WindowType windowType(WId id); + + static QVariant getConf(void *ctx, const char *key, const QVariant &dflt); + bool loadGreetPlugin(); + + bool mInitialLock; + bool mLocked; + int mLockGrace; + int mPriority; + bool mBusy; + KProcess mHackProc; + org::kde::plasmaoverlay::App *mPlasmaDBus; + QDBusServiceWatcher *mServiceWatcher; + bool mPlasmaEnabled; + bool mSetupMode; + QString mSaverExec; + QString mSaver; + bool mOpenGLVisual; + bool child_saver; + QList child_sockets; + int mParent; + bool mUseBlankOnly; + bool mSuspended; + QTimer mSuspendTimer; + bool mVisibility; + bool mDPMSDepend; + QTimer mCheckDPMS; + QStack< QWidget* > mDialogs; + QHash< QWidget*, QWidget* > mFrames; + QList mVisibleDialogs; + QQueue mEventQueue; + bool mEventRecursed; + bool mRestoreXF86Lock; + bool mForbidden; + QStringList mPlugins, mPluginOptions; + QString mMethod; + GreeterPluginHandle greetPlugin; + QPixmap mSavedScreen; + QTimer mSnapshotTimer; + int mAutoLogoutTimerId; + int mAutoLogoutTimeout; + QTimer mSuppressUnlock; + int mSuppressUnlockTimeout; + QList mForeignWindows; + QList mForeignInputWindows; + struct WindowInfo + { + Window window; + bool viewable; + }; + QList windowInfo; +}; + +#endif + diff --git a/kwin/screenlocker/lock/main.cc b/kwin/screenlocker/lock/main.cc new file mode 100644 index 0000000..7b41024 --- /dev/null +++ b/kwin/screenlocker/lock/main.cc @@ -0,0 +1,204 @@ +/* This file is part of the KDE project + Copyright (C) 1999 David Faure + Copyright 2003 Oswald Buddenhagen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "lockprocess.h" +#include "main.h" +#include "kscreensaversettings.h" + +#include +#include +#include +#include +#include +#include +#include "kscreensaver_interface.h" + +#include + +#include +#include +#include + +#include +#include + +bool MyApp::x11EventFilter( XEvent *ev ) +{ + if (ev->type == XKeyPress || ev->type == ButtonPress) + emit activity(); + else if (ev->type == MotionNotify) { + time_t tick = time( 0 ); + if (tick != lastTick) { + lastTick = tick; + emit activity(); + } + } + return KApplication::x11EventFilter( ev ); +} + + +// ----------------------------------------------------------------------------- + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init(argc, argv, "kscreenlocker", "krunner", ki18n("KDE Screen Locker"), + "2.0" , ki18n("Session Locker for KDE Workspace")); + + KCmdLineOptions options; + options.add("forcelock", ki18n("Force session locking")); + options.add("dontlock", ki18n("Only start screen saver")); + options.add("showunlock", ki18n("Immediately show the unlock dialog")); + options.add("blank", ki18n("Only use the blank screen saver")); + options.add("plasmasetup", ki18n("start with plasma unlocked for configuring")); + options.add("daemon", ki18n("Fork into the background after starting up")); + KCmdLineArgs::addCmdLineOptions( options ); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + bool daemonize = false; + int daemonPipe[2]; + char daemonBuf; + if (args->isSet("daemon")) { + daemonize = true; + if (pipe(daemonPipe)) + kFatal() << "pipe() failed"; + switch (fork()) { + case -1: + kFatal() << "fork() failed"; + case 0: + break; + default: + if (read(daemonPipe[0], &daemonBuf, 1) != 1) + _exit(1); + _exit(0); + } + } + + putenv(strdup("SESSION_MANAGER=")); + + //KApplication::disableAutoDcopRegistration(); + + int kdesktop_screen_number = 0; + int starting_screen = 0; + + bool child = false; + int parent_connection = 0; // socket to the parent saver + QList child_sockets; + + if (KGlobalSettings::isMultiHead()) + { + Display *dpy = XOpenDisplay(NULL); + if (! dpy) { + fprintf(stderr, + "%s: FATAL ERROR: could not open display '%s'\n", + argv[0], XDisplayName(NULL)); + exit(1); + } + + int number_of_screens = ScreenCount(dpy); + starting_screen = kdesktop_screen_number = DefaultScreen(dpy); + int pos; + QByteArray display_name = XDisplayString(dpy); + XCloseDisplay(dpy); + kDebug() << "screen " << number_of_screens << " " << kdesktop_screen_number << " " << display_name << " " << starting_screen; + dpy = 0; + + if ((pos = display_name.lastIndexOf('.')) != -1) + display_name.remove(pos, 10); + + QString env; + if (number_of_screens != 1) { + for (int i = 0; i < number_of_screens; i++) { + if (i != starting_screen) { + int fd[2]; + if (pipe(fd)) { + perror("pipe"); + break; + } + if (fork() == 0) { + child = true; + kdesktop_screen_number = i; + parent_connection = fd[0]; + // break here because we are the child process, we don't + // want to fork() anymore + break; + } else { + child_sockets.append(fd[1]); + } + } + } + + env.sprintf("DISPLAY=%s.%d", display_name.data(), + kdesktop_screen_number); + kDebug() << "env " << env; + + if (putenv(strdup(env.toLatin1().data()))) { + fprintf(stderr, + "%s: WARNING: unable to set DISPLAY environment variable\n", + argv[0]); + perror("putenv()"); + } + } + } + + MyApp app; + kDebug() << "app " << kdesktop_screen_number << " " << starting_screen << " " << child << " " << child_sockets.count() << " " << parent_connection; + app.disableSessionManagement(); + app.setQuitOnLastWindowClosed( false ); + KGlobal::locale()->insertCatalog(QLatin1String( "libkworkspace" )); + + LockProcess process(child, args->isSet("blank")); + if (!child) + process.setChildren(child_sockets); + else + process.setParent(parent_connection); + + bool rt; + bool sig = false; + if (!child && (args->isSet("forcelock"))) { + rt = process.lock(args->isSet("showunlock")); + sig = true; + } + else if( child || args->isSet( "dontlock" )) + rt = process.dontLock(); + else if (args->isSet("plasmasetup")) { + rt = process.startSetup(); + } + else + rt = process.defaultSave(); + if (!rt) + return 1; + + if( sig ) + { + org::kde::screensaver kscreensaver(QLatin1String( "org.kde.screensaver" ), QLatin1String( "/ScreenSaver" ), QDBusConnection::sessionBus()); + kscreensaver.saverLockReady(); + } + args->clear(); + if (daemonize) { + daemonBuf = 0; + write(daemonPipe[1], &daemonBuf, 1); + } + return app.exec(); +} + +#include "main.moc" + +#define KDM_NO_SHUTDOWN +#include diff --git a/kwin/screenlocker/lock/main.h b/kwin/screenlocker/lock/main.h new file mode 100644 index 0000000..8a60353 --- /dev/null +++ b/kwin/screenlocker/lock/main.h @@ -0,0 +1,39 @@ +/* This file is part of the KDE project + Copyright 2003 Oswald Buddenhagen + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _MAIN_H +#define _MAIN_H + +#include + +#include + +class MyApp : public KApplication { + Q_OBJECT +public: + MyApp() : KApplication(), lastTick( 0 ) {} +protected: + bool x11EventFilter( XEvent * ); +Q_SIGNALS: + void activity(); +private: + time_t lastTick; +}; + +#endif diff --git a/kwin/screenlocker/screenlocker.cpp b/kwin/screenlocker/screenlocker.cpp new file mode 100644 index 0000000..7994b83 --- /dev/null +++ b/kwin/screenlocker/screenlocker.cpp @@ -0,0 +1,119 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 "screenlocker.h" +#include "saverengine.h" +#include "workspace.h" +#include +#include "effects.h" +// Qt +#include +#include +// KDE +#include +#include +#include +#include +#include +#include + +namespace KWin +{ +namespace ScreenLocker +{ + +ScreenLocker::ScreenLocker::ScreenLocker(QObject *parent) + : QObject(parent) + , m_saverEngine(new SaverEngine(this)) + , m_locked(false) +{ + // TODO: test whether lock file exists +} + +ScreenLocker::~ScreenLocker() +{ +} + +void ScreenLocker::initShortcuts(KActionCollection *keys) +{ + if (KAuthorized::authorize(QLatin1String("lock_screen"))) { + // first make krunner forget its old shortcut + // we do this directly using the D-Bus interface, as KGlobalAccel/KAction has + // no nice way of doing this (other than registering and deregistering the + // krunner shortcut every time) + QDBusInterface accelIface("org.kde.kglobalaccel", "/kglobalaccel", "org.kde.KGlobalAccel"); + QStringList krunnerShortcutId; + krunnerShortcutId << QLatin1String("krunner") << QLatin1String("Lock Session") << "" << ""; + QDBusReply > reply = accelIface.call("shortcut", krunnerShortcutId); + int shortcut = -1; + if (reply.isValid() && reply.value().size() == 1) { + shortcut = reply.value().at(0); + kDebug(1212) << "Existing krunner shortcut for Lock Session found:" << KShortcut(shortcut).toString(); + } + accelIface.call(QDBus::NoBlock, "unRegister", krunnerShortcutId); + + KAction *a = keys->addAction(QLatin1String("Lock Session")); + a->setText(i18n("Lock Session")); + a->setGlobalShortcut(KShortcut(Qt::ALT+Qt::CTRL+Qt::Key_L)); + if (shortcut >= 0) { + // if there was a krunner shortcut, use that + a->setGlobalShortcut(KShortcut(shortcut), KAction::ActiveShortcut, KAction::NoAutoloading); + } + connect(a, SIGNAL(triggered(bool)), this, SLOT(lock())); + } +} + +void ScreenLocker::lock() +{ + if (m_locked) { + return; + } + // TODO: create lock file + bool hasLock = false; + if (Workspace::self()->compositingActive() && static_cast(effects)->provides(Effect::ScreenLocking)) { + // try locking through an Effect + hasLock = static_cast(effects)->lockScreen(); + } + if (!hasLock) { + // no Effect to lock the screen, try legacy X Screen Saver for locking + hasLock = !m_saverEngine->doLock(); + } + if (!hasLock) { + // no working lock implementation + // TODO: remove lock file + return; + } + m_locked = true; + emit locked(); +} + +void ScreenLocker::unlock() +{ + if (!m_locked) { + return; + } + // TODO: remove lock file + // TODO: if compositing was enforced, remove the blocking + m_locked = false; + emit unlocked(); +} + + +} // namespace ScreenLocker +} // namespace KWin diff --git a/kwin/screenlocker/screenlocker.h b/kwin/screenlocker/screenlocker.h new file mode 100644 index 0000000..c156e39 --- /dev/null +++ b/kwin/screenlocker/screenlocker.h @@ -0,0 +1,92 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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_SCREENLOCKER_SCREENLOCKER_H +#define KWIN_SCREENLOCKER_SCREENLOCKER_H + +#include + +// forward declarations +class KActionCollection; +class SaverEngine; + +namespace KWin +{ +namespace ScreenLocker +{ + +/** + * @short Class handling screen locking. + * + * The classic Screen Saving is handled by the @link SaverEngine. + * This class only takes care of locking the screen. That is ensure that + * nothing is displayed on the screen. + * + * The D-Bus interface to screen locking is provided by the @link SaverEngine + * for backwards-compatibility. This class cannot implement the org.freedesktop.ScreenSaver + * interface as it does not provide the screen saving capabilities. + **/ +class ScreenLocker : public QObject +{ + Q_OBJECT +public: + ScreenLocker(QObject *parent = NULL); + virtual ~ScreenLocker(); + + void initShortcuts(KActionCollection *keys); + + /** + * Unlocks the screen. Inside KWin we trust each other and assume + * that the method will only be called when the screen got unlocked + * by a trusted authority. E.g. a KWin Effect or the SaverEngine. + **/ + void unlock(); + /** + * @returns Whether the screen is locked. + **/ + bool isLocked() const { + return m_locked; + } + +Q_SIGNALS: + /** + * Emitted when the screen gets locked. + **/ + void locked(); + /** + * Emitted when the screen gets unlocked. + **/ + void unlocked(); + +public Q_SLOTS: + /** + * Locks the screen, either through a KWin effect (modern) or ScreenSaver Engine (legacy). + **/ + void lock(); + +private: + // legacy screen saver engine. + SaverEngine *m_saverEngine; + // indicates whether the screen is locked + bool m_locked; +}; +} // namespace ScreenLocker +} // namespace KWin + +#endif diff --git a/kwin/screenlocker/screensaver/saverengine.cpp b/kwin/screenlocker/screensaver/saverengine.cpp new file mode 100644 index 0000000..e6ad800 --- /dev/null +++ b/kwin/screenlocker/screensaver/saverengine.cpp @@ -0,0 +1,513 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// + + +#include "saverengine.h" +#include "kscreensaversettings.h" +#include "screensaveradaptor.h" +#include "kscreensaveradaptor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xautolock_c.h" +#include "screenlocker.h" +#include +extern xautolock_corner_t xautolock_corners[ 4 ]; + +//=========================================================================== +// +// Screen saver engine. Doesn't handle the actual screensaver window, +// starting screensaver hacks, or password entry. That's done by +// a newly started process. +// +SaverEngine::SaverEngine(KWin::ScreenLocker::ScreenLocker *locker) + : QWidget() + , m_screenLocker(locker) +{ + (void) new ScreenSaverAdaptor( this ); + QDBusConnection::sessionBus().registerService( QLatin1String( "org.freedesktop.ScreenSaver" ) ) ; + (void) new KScreenSaverAdaptor( this ); + QDBusConnection::sessionBus().registerService( QLatin1String( "org.kde.screensaver" ) ) ; + QDBusConnection::sessionBus().registerObject( QLatin1String( "/ScreenSaver" ), this ); + + // Save X screensaver parameters + XGetScreenSaver(QX11Info::display(), &mXTimeout, &mXInterval, + &mXBlanking, &mXExposures); + // And disable it. The internal X screensaver is not used at all, but we use its + // internal idle timer (and it is also used by DPMS support in X). This timer must not + // be altered by this code, since e.g. resetting the counter after activating our + // screensaver would prevent DPMS from activating. We use the timer merely to detect + // user activity. + XSetScreenSaver(QX11Info::display(), 0, mXInterval, mXBlanking, mXExposures); + + mState = Waiting; + mXAutoLock = 0; + mLockProcess = 0; + + m_nr_throttled = 0; + m_nr_inhibited = 0; + m_actived_time = -1; + + m_serviceWatcher = new QDBusServiceWatcher(this); + m_serviceWatcher->setConnection(QDBusConnection::sessionBus()); + m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + connect(m_serviceWatcher, SIGNAL(serviceUnregistered(QString)), + this, SLOT(serviceUnregistered(QString))); + + // Also receive updates triggered through the DBus (from powerdevil) see Bug #177123 + QStringList modules; + QDBusInterface kdedInterface(QLatin1String( "org.kde.kded" ), QLatin1String( "/kded" ), QLatin1String( "org.kde.kded" )); + QDBusReply reply = kdedInterface.call(QLatin1String( "loadedModules" )); + + if (!reply.isValid()) { + return; + } + + modules = reply.value(); + + if (modules.contains(QLatin1String( "powerdevil" ))) { + if (!QDBusConnection::sessionBus().connect(QLatin1String( "org.kde.kded" ), QLatin1String( "/modules/powerdevil" ), QLatin1String( "org.kde.PowerDevil" ), + QLatin1String( "DPMSconfigUpdated" ), this, SLOT(configure()))) { + kDebug() << "error!"; + } + } + + // I make it a really random number to avoid + // some assumptions in clients, but just increase + // while gnome-ss creates a random number every time + m_next_cookie = KRandom::random() % 20000; + configure(); +} + +//--------------------------------------------------------------------------- +// +// Destructor - usual cleanups. +// +SaverEngine::~SaverEngine() +{ + delete mXAutoLock; + // Just let mLockProcess leak, so the saver is not killed + + // Restore X screensaver parameters + XSetScreenSaver(QX11Info::display(), mXTimeout, mXInterval, mXBlanking, + mXExposures); +} + +//--------------------------------------------------------------------------- + +void SaverEngine::Lock() +{ + m_screenLocker->lock(); +} + +bool SaverEngine::doLock() +{ + if (mState == Waiting) + { + return startLockProcess( ForceLock ); + } + else + { + // XXX race condition here + ::kill(mLockProcess->pid(), SIGHUP); + return false; + } +} + +void SaverEngine::processLockTransactions() +{ + QList::ConstIterator it = mLockTransactions.constBegin(), + end = mLockTransactions.constEnd(); + for ( ; it != end; ++it ) + { + QDBusConnection::sessionBus().send(*it); + } + mLockTransactions.clear(); +} + +void SaverEngine::saverLockReady() +{ + if( mState != Preparing ) + { + kDebug() << "Got unexpected saverLockReady()"; + return; + } + kDebug() << "Saver Lock Ready"; + processLockTransactions(); + if (m_nr_throttled) + ::kill(mLockProcess->pid(), SIGSTOP); +} + +void SaverEngine::SimulateUserActivity() +{ + XForceScreenSaver( QX11Info::display(), ScreenSaverReset ); + if ( mXAutoLock && mState == Waiting ) + { + mXAutoLock->resetTrigger(); + } + else if (mLockProcess) + { + ::kill(mLockProcess->pid(), SIGUSR1); + } +} + +//--------------------------------------------------------------------------- +bool SaverEngine::save() +{ + if (mState == Waiting) + { + return startLockProcess( DefaultLock ); + } + return false; +} + +bool SaverEngine::setupPlasma() +{ + if (mState == Waiting) + { + return startLockProcess( PlasmaSetup ); + } + return false; +} + +//--------------------------------------------------------------------------- +bool SaverEngine::quit() +{ + if (mState == Saving || mState == Preparing) + { + stopLockProcess(); + return true; + } + return false; +} + +//--------------------------------------------------------------------------- +bool SaverEngine::isEnabled() +{ + return mXAutoLock != 0; +} + +//--------------------------------------------------------------------------- +bool SaverEngine::enable( bool e, bool force ) +{ + if ( !force && e == isEnabled() ) + return true; + + // If we aren't in a suitable state, we will not reconfigure. + if (mState != Waiting) + return false; + + if (e) + { + if (!mXAutoLock) + { + mXAutoLock = new XAutoLock(); + connect(mXAutoLock, SIGNAL(timeout()), SLOT(idleTimeout())); + } + + int timeout = KScreenSaverSettings::timeout(); + mXAutoLock->setTimeout(timeout); + mXAutoLock->setDPMS(true); +#ifdef NOT_FREAKIN_UGLY + mXAutoLock->changeCornerLockStatus( mLockCornerTopLeft, mLockCornerTopRight, mLockCornerBottomLeft, mLockCornerBottomRight); +#else + xautolock_corners[0] = applyManualSettings(KScreenSaverSettings::actionTopLeft()); + xautolock_corners[1] = applyManualSettings(KScreenSaverSettings::actionTopRight()); + xautolock_corners[2] = applyManualSettings(KScreenSaverSettings::actionBottomLeft()); + xautolock_corners[3] = applyManualSettings(KScreenSaverSettings::actionBottomRight()); +#endif + + mXAutoLock->start(); + kDebug() << "Saver Engine started, timeout: " << timeout; + } + else + { + delete mXAutoLock; + mXAutoLock = 0; + kDebug() << "Saver Engine disabled"; + } + + return true; +} + +//--------------------------------------------------------------------------- +bool SaverEngine::isBlanked() +{ + return (mState != Waiting); +} + +//--------------------------------------------------------------------------- +// +// Read and apply configuration. +// +void SaverEngine::configure() +{ + // create a new config obj to ensure we read the latest options + KScreenSaverSettings::self()->readConfig(); + + enable( KScreenSaverSettings::screenSaverEnabled(), true ); +} + +//--------------------------------------------------------------------------- +// +// Start the screen saver. +// +bool SaverEngine::startLockProcess( LockType lock_type ) +{ + Q_ASSERT(mState == Waiting); + + kDebug() << "SaverEngine: starting saver"; + + QString path = KStandardDirs::findExe( QLatin1String( "kscreenlocker" ) ); + if( path.isEmpty()) + { + kDebug() << "Can't find kscreenlocker!"; + return false; + } + mLockProcess = new KProcess; // No parent, so it is not auto-deleted + connect(mLockProcess, SIGNAL(finished(int,QProcess::ExitStatus)), + SLOT(lockProcessExited())); + *mLockProcess << path; + switch( lock_type ) + { + case ForceLock: + *mLockProcess << QLatin1String( "--forcelock" ); + break; + case DontLock: + *mLockProcess << QLatin1String( "--dontlock" ); + break; + case PlasmaSetup: + *mLockProcess << QLatin1String( "--plasmasetup" ); + break; + default: + break; + } + + m_actived_time = time( 0 ); + mLockProcess->start(); + if (mLockProcess->waitForStarted() == false ) + { + kDebug() << "Failed to start kscreenlocker!"; + delete mLockProcess; + mLockProcess = 0; + m_actived_time = -1; + return false; + } + + if (mXAutoLock) + { + mXAutoLock->stop(); + } + + emit ActiveChanged(true); // DBus signal + mState = Preparing; + + // It takes a while for kscreenlocker to start and lock the screen. + // Therefore delay the DBus call until it tells krunner that the locking is in effect. + // This is done only for --forcelock . + if (lock_type == ForceLock && calledFromDBus()) { + mLockTransactions.append(message().createReply()); + setDelayedReply(true); + } + + return true; +} + +//--------------------------------------------------------------------------- +// +// Stop the screen saver. +// +void SaverEngine::stopLockProcess() +{ + Q_ASSERT(mState != Waiting); + kDebug() << "SaverEngine: stopping lock process"; + + mLockProcess->kill(); +} + +void SaverEngine::lockProcessExited() +{ + Q_ASSERT(mState != Waiting); + kDebug() << "SaverEngine: lock process exited"; + + delete mLockProcess; + mLockProcess = 0; + + if (mXAutoLock) + { + mXAutoLock->start(); + } + + processLockTransactions(); + emit ActiveChanged(false); // DBus signal + m_actived_time = -1; + mState = Waiting; + m_screenLocker->unlock(); +} + +//--------------------------------------------------------------------------- +// +// XAutoLock has detected the required idle time. +// +void SaverEngine::idleTimeout() +{ + if( mState != Waiting ) + return; // already saving + + m_screenLocker->lock(); +} + +xautolock_corner_t SaverEngine::applyManualSettings(int action) +{ + if (action == 0) + { + kDebug() << "no lock"; + return ca_nothing; + } + else if (action == 1) + { + kDebug() << "lock screen"; + return ca_forceLock; + } + else if (action == 2) + { + kDebug() << "prevent lock"; + return ca_dontLock; + } + else + { + kDebug() << "no lock nothing"; + return ca_nothing; + } +} + +uint SaverEngine::GetSessionIdleTime() +{ + return mXAutoLock ? mXAutoLock->idleTime() : 0; +} + +uint SaverEngine::GetActiveTime() +{ + if ( m_actived_time == -1 ) + return 0; + return time( 0 ) - m_actived_time; +} + +bool SaverEngine::GetActive() +{ + return ( mState != Waiting ); +} + +bool SaverEngine::SetActive(bool state) +{ + if ( state ) + return save(); + else + return quit(); +} + +uint SaverEngine::Inhibit(const QString &/*application_name*/, const QString &/*reason*/) +{ + ScreenSaverRequest sr; +// sr.appname = application_name; +// sr.reasongiven = reason; + sr.cookie = m_next_cookie++; + sr.dbusid = message().service(); + sr.type = ScreenSaverRequest::Inhibit; + m_requests.append( sr ); + m_serviceWatcher->addWatchedService(sr.dbusid); + m_nr_inhibited++; + if (KScreenSaverSettings::screenSaverEnabled()) + enable( false ); + return sr.cookie; +} + +void SaverEngine::UnInhibit(uint cookie) +{ + QMutableListIterator it( m_requests ); + while ( it.hasNext() ) + { + if ( it.next().cookie == cookie ) { + it.remove(); + if ( !--m_nr_inhibited ) + if (KScreenSaverSettings::screenSaverEnabled()) + enable( true ); + } + } +} + +uint SaverEngine::Throttle(const QString &/*application_name*/, const QString &/*reason*/) +{ + ScreenSaverRequest sr; +// sr.appname = application_name; +// sr.reasongiven = reason; + sr.cookie = m_next_cookie++; + sr.type = ScreenSaverRequest::Throttle; + sr.dbusid = message().service(); + m_requests.append( sr ); + m_serviceWatcher->addWatchedService(sr.dbusid); + m_nr_throttled++; + if (mLockProcess) + // XXX race condition here (locker may be not ready yet) + ::kill(mLockProcess->pid(), SIGSTOP); + return sr.cookie; +} + +void SaverEngine::UnThrottle(uint cookie) +{ + QMutableListIterator it( m_requests ); + while ( it.hasNext() ) + { + if ( it.next().cookie == cookie ) { + it.remove(); + if ( !--m_nr_throttled ) + if (mLockProcess) + ::kill(mLockProcess->pid(), SIGCONT); + } + } +} + +void SaverEngine::serviceUnregistered(const QString& name) +{ + m_serviceWatcher->removeWatchedService( name ); + QListIterator it( m_requests ); + while ( it.hasNext() ) + { + const ScreenSaverRequest &r = it.next(); + if ( r.dbusid == name ) + { + if ( r.type == ScreenSaverRequest::Throttle ) + UnThrottle( r.cookie ); + else + UnInhibit( r.cookie ); + } + } +} + +#include "saverengine.moc" + + diff --git a/kwin/screenlocker/screensaver/saverengine.h b/kwin/screenlocker/screensaver/saverengine.h new file mode 100644 index 0000000..4efebec --- /dev/null +++ b/kwin/screenlocker/screensaver/saverengine.h @@ -0,0 +1,195 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// + +#ifndef SAVERENGINE_H +#define SAVERENGINE_H + +#include +#include +#include +#include + +class QDBusServiceWatcher; +class KProcess; + +namespace KWin { +namespace ScreenLocker { + class ScreenLocker; +} +} + +#include "xautolock.h" +#include "xautolock_c.h" + +class ScreenSaverRequest +{ +public: +// QString appname; +// QString reasongiven; + QString dbusid; + uint cookie; + enum { Inhibit,Throttle } type; +}; + +//=========================================================================== +/** + * Screen saver engine. Handles screensaver window, starting screensaver + * hacks, and password entry. + */ +class SaverEngine : public QWidget, protected QDBusContext +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.ScreenSaver") + +public: + SaverEngine(KWin::ScreenLocker::ScreenLocker *locker); + ~SaverEngine(); + + bool doLock(); + +public Q_SLOTS: + /** + * Lock the screen now even if the screensaver does not lock by default. + */ + void Lock(); + + /** + * Save the screen now. If the user has locking enabled, the screen is locked also. + */ + bool save(); + + /** + * Start the screensaver in plasma-setup mode. + * if plasma is not enabled, this just locks the screen. + */ + bool setupPlasma(); + + /** + * Quit the screensaver if it is running + */ + bool quit(); + + /** + * Simulate user activity + */ + void SimulateUserActivity(); + + /** + * Return true if the screensaver is enabled + */ + bool isEnabled(); + + /** + * Enable/disable the screensaver + * @return true if the action succeeded + */ + bool enable( bool e, bool force = false ); + + /** + * Return true if the screen is currently blanked + */ + bool isBlanked(); + + /** + * Read and apply configuration. + */ + void configure(); + + /** + * Called by krunner_lock when locking is in effect. + */ + void saverLockReady(); + + /** + * Request a change in the state of the screensaver. + * Set to TRUE to request that the screensaver activate. + * Active means that the screensaver has blanked the + * screen and may run a graphical theme. This does + * not necessary mean that the screen is locked. + */ + bool SetActive( bool state ); + + /// Returns the value of the current state of activity (See setActive) + bool GetActive(); + + /** + * Returns the number of seconds that the screensaver has + * been active. Returns zero if the screensaver is not active. + */ + uint GetActiveTime(); + + /** + * Returns the number of seconds that the session has + * been idle. Returns zero if the session is not idle. + */ + uint GetSessionIdleTime(); + + /** + * Request that saving the screen due to system idleness + * be blocked until UnInhibit is called or the + * calling process exits. + * The cookie is a random number used to identify the request + */ + uint Inhibit(const QString &application_name, const QString &reason_for_inhibit); + /// Cancel a previous call to Inhibit() identified by the cookie. + void UnInhibit(uint cookie); + + /** + * Request that running themes while the screensaver is active + * be blocked until UnThrottle is called or the + * calling process exits. + * The cookie is a random number used to identify the request + */ + uint Throttle(const QString &application_name, const QString &reason_for_inhibit); + /// Cancel a previous call to Throttle() identified by the cookie. + void UnThrottle(uint cookie); + +Q_SIGNALS: + // DBus signals + void ActiveChanged(bool state); + +protected Q_SLOTS: + void idleTimeout(); + void lockProcessExited(); + void serviceUnregistered(const QString&); + +protected: + enum LockType { DontLock, DefaultLock, ForceLock, PlasmaSetup }; + bool startLockProcess( LockType lock_type ); + void stopLockProcess(); + bool handleKeyPress(XKeyEvent *xke); + void processLockTransactions(); + xautolock_corner_t applyManualSettings(int); + +private: + enum State { Waiting, Preparing, Saving }; + + State mState; + XAutoLock *mXAutoLock; + KProcess *mLockProcess; + + // the original X screensaver parameters + int mXTimeout; + int mXInterval; + int mXBlanking; + int mXExposures; + + time_t m_actived_time; + QList m_requests; + QDBusServiceWatcher *m_serviceWatcher; + uint m_next_cookie; + + int m_nr_throttled; + int m_nr_inhibited; + + QList mLockTransactions; + + KWin::ScreenLocker::ScreenLocker *m_screenLocker; +}; + +#endif + diff --git a/kwin/screenlocker/screensaver/xautolock.cpp b/kwin/screenlocker/screensaver/xautolock.cpp new file mode 100644 index 0000000..7124215 --- /dev/null +++ b/kwin/screenlocker/screensaver/xautolock.cpp @@ -0,0 +1,317 @@ +//---------------------------------------------------------------------------- +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// Copyright 2003 Lubos Lunak +// +// KDE screensaver engine +// + +#include + +#include "xautolock.h" +#include "xautolock_c.h" + +#include +#include + +#include +#include + +#include +#include + +#ifdef HAVE_DPMS +extern "C" { +#include +#ifndef Bool +#define Bool BOOL +#endif +#include + +#ifndef HAVE_DPMSINFO_PROTO +Status DPMSInfo ( Display *, CARD16 *, BOOL * ); +#endif +} +#endif + +#include + +xautolock_corner_t xautolock_corners[ 4 ]; + +static XAutoLock* self = NULL; + +extern "C" { +static int catchFalseAlarms(Display *, XErrorEvent *) +{ + return 0; +} +} + +//=========================================================================== +// +// Detect user inactivity. +// Named XAutoLock after the program that it is based on. +// +XAutoLock::XAutoLock() +{ + self = this; +#ifdef HAVE_XSCREENSAVER + mMitInfo = 0; + int dummy; + if (XScreenSaverQueryExtension( QX11Info::display(), &dummy, &dummy )) + { + mMitInfo = XScreenSaverAllocInfo(); + } + else +#endif + { + kapp->installX11EventFilter( this ); + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(catchFalseAlarms); + XSync(QX11Info::display(), False ); + xautolock_initDiy( QX11Info::display()); + XSync(QX11Info::display(), False ); + XSetErrorHandler(oldHandler); + } + + mTimeout = DEFAULT_TIMEOUT; + mDPMS = true; + resetTrigger(); + + mActive = false; + + mTimerId = startTimer( CHECK_INTERVAL ); + // This is an internal clock timer (in seconds), used instead of querying system time. + // It is incremented manually, preventing from problems with clock jumps. + // In other words, this is the 'now' time and the reference point for other times here. + mElapsed = 0; +} + +//--------------------------------------------------------------------------- +// +// Destructor. +// +XAutoLock::~XAutoLock() +{ + stop(); + self = NULL; +} + +//--------------------------------------------------------------------------- +// +// The time in seconds of continuous inactivity. +// +void XAutoLock::setTimeout(int t) +{ + mTimeout = t; +} + +void XAutoLock::setDPMS(bool s) +{ +#ifdef HAVE_DPMS + BOOL on; + CARD16 state; + DPMSInfo( QX11Info::display(), &state, &on ); + if (!on) + s = false; +#endif + mDPMS = s; +} + +//--------------------------------------------------------------------------- +// +// Start watching Activity +// +void XAutoLock::start() +{ + mActive = true; + resetTrigger(); +} + +//--------------------------------------------------------------------------- +// +// Stop watching Activity +// +void XAutoLock::stop() +{ + mActive = false; + resetTrigger(); +} + +//--------------------------------------------------------------------------- +// +// Reset the trigger time. +// +void XAutoLock::resetTrigger() +{ + // Time of the last user activity (used only when the internal XScreensaver + // idle counter is not available). + mLastReset = mElapsed; + // Time when screensaver should be activated. + mTrigger = mElapsed + mTimeout; +#ifdef HAVE_XSCREENSAVER + mLastIdle = 0; +#endif + // Do not reset the internal X screensaver here (no XForceScreenSaver()) +} + +//--------------------------------------------------------------------------- +// +// Move the trigger time in order to postpone (repeat) emitting of timeout(). +// +void XAutoLock::postpone() +{ + mTrigger = mElapsed + 60; // delay by 60sec +} + +//--------------------------------------------------------------------------- +// +// Set the remaining time to 't', if it's shorter than already set. +// +void XAutoLock::setTrigger( int t ) +{ + time_t newT = mElapsed + qMax(t, 0); + if (mTrigger > newT) + mTrigger = newT; +} + +//--------------------------------------------------------------------------- +// +// Process new windows and check the mouse. +// +void XAutoLock::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() != mTimerId) + { + return; + } + mElapsed += CHECK_INTERVAL / 1000; + +#ifdef HAVE_XSCREENSAVER + if (!mMitInfo) +#endif + { // only the diy way needs special X handler + XSync( QX11Info::display(), False ); + int (*oldHandler)(Display *, XErrorEvent *) = + XSetErrorHandler(catchFalseAlarms); + + xautolock_processQueue(); + + XSetErrorHandler(oldHandler); + } + +#ifdef HAVE_XSCREENSAVER + if (mMitInfo) + { + Display *d = QX11Info::display(); + // Check user idle time. If it is smaller than before, it is either + // clock jump or user activity, so reset the trigger time. Checking whether + // there is user inactivity timeout is done below using mTrigger and mElapsed. + XScreenSaverQueryInfo(d, DefaultRootWindow(d), mMitInfo); + if (mLastIdle < mMitInfo->idle) + mLastIdle = mMitInfo->idle; + else + resetTrigger(); + } +#endif /* HAVE_XSCREENSAVER */ + + // This needs to be after the above check, so it overrides it. + xautolock_queryPointer( QX11Info::display()); + + bool activate = false; + + // This is the test whether to activate screensaver. If we have reached the time + // and for the whole timeout period there was no activity (which would change mTrigger + // again), activate. + if (mElapsed >= mTrigger) + activate = true; + +#ifdef HAVE_DPMS + BOOL on; + CARD16 state; + CARD16 timeout1, timeout2, timeout3; + DPMSInfo( QX11Info::display(), &state, &on ); + DPMSGetTimeouts( QX11Info::display(), &timeout1, &timeout2, &timeout3 ); + + // kDebug() << "DPMSInfo " << state << on; + // If DPMS is active, it makes XScreenSaverQueryInfo() report idle time + // that is always smaller than DPMS timeout (X bug I guess). So if DPMS + // saving is active, simply always activate our saving too, otherwise + // this could prevent locking from working. + // X.Org 7.4: With this version activating DPMS resets the screensaver idle timer, + // so keep this. It probably makes sense to always do this anyway. + if(state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff) + activate = true; + // If we are DPMS-dependent and either DPMS is turned off completely or all + // three DPMS modes are turned off, don't activate (apps use this to turn off + // screensavers). + if(mDPMS && (!on || (timeout1 == 0 && timeout2 == 0 && timeout3 == 0 ))) { + activate = false; + resetTrigger(); + } +#endif + + // Do not check whether internal X screensaver is enabled or disabled, since we + // have disabled it ourselves. Some apps might try to disable it too to prevent + // screensavers, but then our logic breaks[*]. Those apps need to disable DPMS anyway, + // or they will still have problems, so the DPMS code above should be enough. + // Besides, I doubt other screensaver implementations check this either. + // [*] We can't run with X screensaver enabled, since then sooner or later + // the internal screensaver will activate instead of our screensaver and we cannot + // prevent its activation by resetting the idle counter since that would also + // reset DPMS saving. + + if(mActive && activate) + emit timeout(); +} + +bool XAutoLock::x11Event( XEvent* ev ) +{ + xautolock_processEvent( ev ); +// don't futher process key events that were received only because XAutoLock wants them + if( ev->type == KeyPress && !ev->xkey.send_event +#ifdef HAVE_XSCREENSAVER + && !mMitInfo +#endif + && !QWidget::find( ev->xkey.window )) + return true; + return false; +} + +bool XAutoLock::ignoreWindow( WId w ) +{ + if( w != QX11Info::appRootWindow() && QWidget::find( w )) + return true; + return false; +} + +time_t XAutoLock::idleTime() +{ +#ifdef HAVE_XSCREENSAVER + if (mMitInfo) + return mMitInfo->idle / 1000; +#endif + return mElapsed - mLastReset; +} + +extern "C" +void xautolock_resetTriggers() +{ + self->resetTrigger(); +} + +extern "C" +void xautolock_setTrigger( int t ) +{ + self->setTrigger( t ); +} + +extern "C" +int xautolock_ignoreWindow( Window w ) +{ + return self->ignoreWindow( w ); +} + +#include "xautolock.moc" diff --git a/kwin/screenlocker/screensaver/xautolock.h b/kwin/screenlocker/screensaver/xautolock.h new file mode 100644 index 0000000..3db3233 --- /dev/null +++ b/kwin/screenlocker/screensaver/xautolock.h @@ -0,0 +1,91 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright 1999 Martin R. Jones +// + +#ifndef __XAUTOLOCK_H__ +#define __XAUTOLOCK_H__ + +#include + +#include + +#include +#ifdef HAVE_XSCREENSAVER +# include +#endif +#include + +//=========================================================================== +// +// Detect user inactivity. +// Named XAutoLock after the program that it is based on. +// +class XAutoLock : public QWidget +{ + Q_OBJECT +public: + XAutoLock(); + ~XAutoLock(); + + //----------------------------------------------------------------------- + // + // The time in seconds of continuous inactivity. + // Need to call start() again afterwards. + // + void setTimeout(int t); + + void setDPMS(bool s); + + //----------------------------------------------------------------------- + // + // Start watching Activity + // + void start(); + + //----------------------------------------------------------------------- + // + // Stop watching Activity + // + void stop(); + + //----------------------------------------------------------------------- + // + // Should be called only from a slot connected to the timeout() signal. Will + // result in the timeout() signal being emitted again with a delay (i.e. postponed). + // + void postpone(); + + // internal + void resetTrigger(); + // internal + void setTrigger( int ); + // internal + bool ignoreWindow( WId ); + // internal + time_t idleTime(); + +Q_SIGNALS: + void timeout(); + +protected: + virtual void timerEvent(QTimerEvent *ev); + virtual bool x11Event( XEvent* ); + +protected: + int mTimerId; + int mTimeout; + time_t mTrigger; + bool mActive; + time_t mLastReset; + time_t mElapsed; + bool mDPMS; +#ifdef HAVE_XSCREENSAVER + XScreenSaverInfo *mMitInfo; + ulong mLastIdle; +#endif +}; + +#endif diff --git a/kwin/screenlocker/screensaver/xautolock_c.h b/kwin/screenlocker/screensaver/xautolock_c.h new file mode 100644 index 0000000..3b82f5c --- /dev/null +++ b/kwin/screenlocker/screensaver/xautolock_c.h @@ -0,0 +1,65 @@ +/***************************************************************************** + * + * Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT) + * + * Content: This file is part of version 2.x of xautolock. It takes care + * of most OS dependencies, and defines the program's default + * settings. + * + * Please send bug reports etc. to eyckmans@imec.be. + * + * -------------------------------------------------------------------------- + * + * Copyright 1990,1992-1999,2001-2002 by Stefan De Troch and Michel Eyckmans. + * + * Versions 2.0 and above of xautolock are available under version 2 of the + * GNU GPL. Earlier versions are available under other conditions. For more + * information, see the License file. + * + *****************************************************************************/ + +#ifndef __xautolock_c_h +#define __xautolock_c_h + +#include +#ifdef __cplusplus +# include +#endif + +#define DEFAULT_TIMEOUT 600 + +#define CHECK_INTERVAL 5000 /* ms */ + +#define CREATION_DELAY 30 /* should be > 10 and + < min (45,(MIN_MINUTES*30)) */ +#define TIME_CHANGE_LIMIT 120 /* if the time changes by more + than x secs then we will + assume someone has changed + date or machine has suspended */ + +#define cornerSize 5 + +#define cornerDelay 5 + +#define cornerRedelay 5 + +typedef enum { ca_nothing, ca_dontLock, ca_forceLock } xautolock_corner_t; + +#ifdef __cplusplus +extern "C" +{ +#endif +void xautolock_processEvent( XEvent* ev ); +void xautolock_processQueue( void ); +void xautolock_queryPointer (Display* d); +void xautolock_initDiy (Display* d); +void xautolock_resetTriggers( void ); +void xautolock_setTrigger( int ); +int xautolock_ignoreWindow( Window ); +extern xautolock_corner_t xautolock_corners[ 4 ]; +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/kwin/screenlocker/screensaver/xautolock_diy.c b/kwin/screenlocker/screensaver/xautolock_diy.c new file mode 100644 index 0000000..b9df2f8 --- /dev/null +++ b/kwin/screenlocker/screensaver/xautolock_diy.c @@ -0,0 +1,289 @@ +/***************************************************************************** + * + * Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT) + * + * Content: This file is part of version 2.x of xautolock. It implements + * the stuff used when the program is not using a screen saver + * extension and thus has to use the good old "do it yourself" + * approach for detecting user activity. + * + * The basic idea is that we initially traverse the window tree, + * selecting SubstructureNotify on all windows and adding each + * window to a temporary list. About +- 30 seconds later, we + * scan this list, now asking for KeyPress events. The delay + * is needed in order to interfere as little as possible with + * the event propagation mechanism. Whenever a new window is + * created by an application, a similar process takes place. + * + * Please send bug reports etc. to eyckmans@imec.be. + * + * -------------------------------------------------------------------------- + * + * Copyright 1990,1992-1999,2001-2002 by Stefan De Troch and Michel Eyckmans. + * + * Versions 2.0 and above of xautolock are available under version 2 of the + * GNU GPL. Earlier versions are available under other conditions. For more + * information, see the License file. + * + *****************************************************************************/ + +#include +#include +#include + +#include "xautolock_c.h" + +static void selectEvents (Window window, Bool substructureOnly); + +/* + * Window queue management. + */ +typedef struct item +{ + Window window; + time_t creationtime; + struct item* next; +} xautolock_anItem, *xautolock_item; + +static struct +{ + Display* display; + struct item* head; + struct item* tail; +} queue; + +static void +addToQueue (Window window) +{ + xautolock_item newItem = malloc(sizeof(xautolock_anItem)); + + newItem->window = window; + newItem->creationtime = time (0); + newItem->next = 0; + + if (!queue.head) queue.head = newItem; + if ( queue.tail) queue.tail->next = newItem; + + queue.tail = newItem; +} + +static void +processQueue (time_t age) +{ + if (queue.head) + { + time_t now = time (0); + xautolock_item current = queue.head; + + while (current && current->creationtime + age < now) + { + selectEvents (current->window, False); + queue.head = current->next; + free (current); + current = queue.head; + } + + if (!queue.head) queue.tail = 0; + } +} + +/* + * Function for selecting all interesting events on a given + * (tree of) window(s). + */ +static void +selectEvents (Window window, Bool substructureOnly) +{ + Window root; /* root window of the window */ + Window parent; /* parent of the window */ + Window* children; /* children of the window */ + unsigned nofChildren = 0; /* number of children */ + unsigned i; /* loop counter */ + XWindowAttributes attribs; /* attributes of the window */ + + if( xautolock_ignoreWindow( window )) + return; + /* + * Start by querying the server about the root and parent windows. + */ + if (!XQueryTree (queue.display, window, &root, &parent, + &children, &nofChildren)) + { + return; + } + + if (nofChildren) (void) XFree ((char*) children); + + /* + * Build the appropriate event mask. The basic idea is that we don't + * want to interfere with the normal event propagation mechanism if + * we don't have to. + * + * On the root window, we need to ask for both substructureNotify + * and KeyPress events. On all other windows, we always need + * substructureNotify, but only need Keypress if some other client + * also asked for them, or if they are not being propagated up the + * window tree. + */ +#if 0 + if (substructureOnly) + { + (void) XSelectInput (queue.display, window, SubstructureNotifyMask); + } + else + { + if (parent == None) /* the *real* rootwindow */ + { + attribs.all_event_masks = + attribs.do_not_propagate_mask = KeyPressMask; + } + else if (!XGetWindowAttributes (queue.display, window, &attribs)) +#else + { + if (!XGetWindowAttributes (queue.display, window, &attribs)) +#endif + { + return; + } + +#if 0 + (void) XSelectInput (queue.display, window, + SubstructureNotifyMask + | ( ( attribs.all_event_masks + | attribs.do_not_propagate_mask) + & KeyPressMask)); +#else + { + int mask = SubstructureNotifyMask | attribs.your_event_mask; + if( !substructureOnly ) + { + mask |= ( ( attribs.all_event_masks + | attribs.do_not_propagate_mask) + & KeyPressMask ); + } + (void) XSelectInput (queue.display, window, mask ); + } +#endif + + } + + /* + * Now ask for the list of children again, since it might have changed + * in between the last time and us selecting SubstructureNotifyMask. + * + * There is a (very small) chance that we might process a subtree twice: + * child windows that have been created after our XSelectinput() has + * been processed but before we get to the XQueryTree() bit will be + * in this situation. This is harmless. It could be avoided by using + * XGrabServer(), but that'd be an impolite thing to do, and since it + * isn't required... + */ + if (!XQueryTree (queue.display, window, &root, &parent, + &children, &nofChildren)) + { + return; + } + + /* + * Now do the same thing for all children. + */ + for (i = 0; i < nofChildren; ++i) + { + selectEvents (children[i], substructureOnly); + } + + if (nofChildren) (void) XFree ((char*) children); +} + +#if 0 +/* + * Function for processing any events that have come in since + * last time. It is crucial that this function does not block + * in case nothing interesting happened. + */ +void +processEvents (void) +{ + while (XPending (queue.display)) + { + XEvent event; + + if (XCheckMaskEvent (queue.display, SubstructureNotifyMask, &event)) + { + if (event.type == CreateNotify) + { + addToQueue (event.xcreatewindow.window); + } + } + else + { + (void) XNextEvent (queue.display, &event); + } + + /* + * Reset the triggers if and only if the event is a + * KeyPress event *and* was not generated by XSendEvent(). + */ + if ( event.type == KeyPress + && !event.xany.send_event) + { + resetTriggers (); + } + } + + /* + * Check the window queue for entries that are older than + * CREATION_DELAY seconds. + */ + processQueue ((time_t) CREATION_DELAY); +} +#else +void xautolock_processEvent( XEvent* event ) +{ + if (event->type == CreateNotify) + { + addToQueue (event->xcreatewindow.window); + } + /* + * Reset the triggers if and only if the event is a + * KeyPress event *and* was not generated by XSendEvent(). + */ + if ( event->type == KeyPress + && !event->xany.send_event) + { + xautolock_resetTriggers (); + } +} + +void xautolock_processQueue() +{ + /* + * Check the window queue for entries that are older than + * CREATION_DELAY seconds. + */ + processQueue ((time_t) CREATION_DELAY); +} +#endif + + +/* + * Function for initialising the whole shebang. + */ +void +xautolock_initDiy (Display* d) +{ + int s; + + queue.display = d; + queue.tail = 0; + queue.head = 0; + + for (s = -1; ++s < ScreenCount (d); ) + { + Window root = RootWindowOfScreen (ScreenOfDisplay (d, s)); + addToQueue (root); +#if 0 + selectEvents (root, True); +#endif + } +} diff --git a/kwin/screenlocker/screensaver/xautolock_engine.c b/kwin/screenlocker/screensaver/xautolock_engine.c new file mode 100644 index 0000000..d6d0cf5 --- /dev/null +++ b/kwin/screenlocker/screensaver/xautolock_engine.c @@ -0,0 +1,137 @@ +/***************************************************************************** + * + * Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT) + * + * Content: This file is part of version 2.x of xautolock. It implements + * the program's core functions. + * + * Please send bug reports etc. to eyckmans@imec.be. + * + * -------------------------------------------------------------------------- + * + * Copyright 1990,1992-1999,2001-2002 by Stefan De Troch and Michel Eyckmans. + * + * Versions 2.0 and above of xautolock are available under version 2 of the + * GNU GPL. Earlier versions are available under other conditions. For more + * information, see the License file. + * + *****************************************************************************/ + +#include "xautolock_c.h" + +/* + * Function for monitoring pointer movements. This implements the + * `corners' feature and as a side effect also tracks pointer + * related user activity. The latter actually is only needed when + * we're using the DIY mode of operations, but it's much simpler + * to do it unconditionally. + */ +void +xautolock_queryPointer (Display* d) +{ + Window dummyWin; /* as it says */ + int dummyInt; /* as it says */ + unsigned mask; /* modifier mask */ + int rootX; /* as it says */ + int rootY; /* as it says */ + int corner; /* corner index */ + int i; /* loop counter */ + static Window root; /* root window the pointer is on */ + static Screen* screen; /* screen the pointer is on */ + static unsigned prevMask = 0; /* as it says */ + static int prevRootX = -1; /* as it says */ + static int prevRootY = -1; /* as it says */ + static Bool firstCall = True; /* as it says */ + + /* + * Have a guess... + */ + if (firstCall) + { + firstCall = False; + root = DefaultRootWindow (d); + screen = ScreenOfDisplay (d, DefaultScreen (d)); + } + + /* + * Find out whether the pointer has moved. Using XQueryPointer for this + * is gross, but it also is the only way never to mess up propagation + * of pointer events. + */ + if (!XQueryPointer (d, root, &root, &dummyWin, &rootX, &rootY, + &dummyInt, &dummyInt, &mask)) + { + /* + * Pointer has moved to another screen, so let's find out which one. + */ + for (i = -1; ++i < ScreenCount (d); ) + { + if (root == RootWindow (d, i)) + { + screen = ScreenOfDisplay (d, i); + break; + } + } + } + + if ( rootX == prevRootX + && rootY == prevRootY + && mask == prevMask) + { + xautolock_corner_t* corners = xautolock_corners; + /* + * If the pointer has not moved since the previous call and + * is inside one of the 4 corners, we act according to the + * contents of the "corners" array. + * + * If rootX and rootY are less than zero, don't lock even if + * ca_forceLock is set in the upper-left corner. Why? 'cause + * on initial server startup, if (and only if) the pointer is + * never moved, XQueryPointer() can return values less than + * zero (only some servers, Openwindows 2.0 and 3.0 in + * particular). + */ + if ( (corner = 0, + rootX <= cornerSize && rootX >= 0 + && rootY <= cornerSize && rootY >= 0) + || (corner++, + rootX >= WidthOfScreen (screen) - cornerSize - 1 + && rootY <= cornerSize) + || (corner++, + rootX <= cornerSize + && rootY >= HeightOfScreen (screen) - cornerSize - 1) + || (corner++, + rootX >= WidthOfScreen (screen) - cornerSize - 1 + && rootY >= HeightOfScreen (screen) - cornerSize - 1)) + { + switch (corners[corner]) + { + case ca_forceLock: +#if 0 + xautolock_setTrigger( (useRedelay ? cornerRedelay : cornerDelay) - 1 ); +#else + xautolock_setTrigger( 0 ); +#endif + break; + + case ca_dontLock: + xautolock_resetTriggers (); + +#ifdef __GNUC__ + default: ; /* Makes gcc -Wall shut up. */ +#endif /* __GNUC__ */ + } + } + } + else + { +#if 0 + useRedelay = False; +#endif + prevRootX = rootX; + prevRootY = rootY; + prevMask = mask; + + xautolock_resetTriggers (); + } +} diff --git a/kwin/useractions.cpp b/kwin/useractions.cpp index d969def..7eba1c5 100644 --- a/kwin/useractions.cpp +++ b/kwin/useractions.cpp @@ -32,6 +32,7 @@ along with this program. If not, see . #include "effects.h" #include "tile.h" #include "tilinglayout.h" +#include "screenlocker/screenlocker.h" #include "kactivityinfo.h" @@ -564,6 +565,8 @@ void Workspace::readShortcuts() { // TODO: PORT ME (KGlobalAccel related) //KGlobalAccel::self()->readSettings(); + keys = new KActionCollection(this); + KActionCollection* actionCollection = keys; KAction *kaction = qobject_cast(keys->action("Walk Through Desktops")); if (kaction != 0) { @@ -626,6 +629,7 @@ void Workspace::readShortcuts() cutWalkThroughWindowsAlternativeReverse = kaction->globalShortcut(); connect(kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughWindowsAlternativeKeyChanged(QKeySequence))); } + m_screenLocker->initShortcuts(actionCollection); discardPopup(); // so that it's recreated next time } diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp index 19508be..19cc1c3 100644 --- a/kwin/workspace.cpp +++ b/kwin/workspace.cpp @@ -61,6 +61,7 @@ along with this program. If not, see . #include "tilinglayout.h" #include "scripting/scripting.h" +#include "screenlocker/screenlocker.h" #include #include @@ -161,6 +162,7 @@ Workspace::Workspace(bool restore) , transButton(NULL) , forceUnredirectCheck(true) , m_finishingCompositing(false) + , m_screenLocker(NULL) { (void) new KWinAdaptor(this); @@ -223,6 +225,8 @@ Workspace::Workspace(bool restore) // need to create the tabbox before compositing scene is setup tab_box = new TabBox::TabBox(this); setupCompositing(); + // ScreenLocker needs to be created after compositing + m_screenLocker = new ScreenLocker::ScreenLocker(this); // Compatibility long data = 1; diff --git a/kwin/workspace.h b/kwin/workspace.h index 83e1591..ba6ff53 100644 --- a/kwin/workspace.h +++ b/kwin/workspace.h @@ -41,7 +41,6 @@ along with this program. If not, see . #include "sm.h" #include - // TODO: Cleanup the order of things in this .h file class QMenu; @@ -61,6 +60,10 @@ namespace Kephal } namespace KWin { +namespace ScreenLocker +{ +class ScreenLocker; +} namespace TabBox { @@ -450,6 +453,7 @@ public: bool rulesUpdatesDisabled() const; bool hasDecorationShadows() const; + Qt::Corner decorationCloseButtonCorner(); bool decorationHasAlpha() const; bool decorationSupportsClientGrouping() const; // Returns true if the decoration supports tabs. bool decorationSupportsFrameOverlap() const; @@ -560,6 +564,13 @@ public: void raiseElectricBorderWindows(); + ScreenLocker::ScreenLocker *screenLocker() { + return m_screenLocker; + } + const ScreenLocker::ScreenLocker *screenLocker() const { + return m_screenLocker; + } + public slots: void addRepaintFull(); void refresh(); @@ -1049,6 +1060,8 @@ private: QTimer compositeResetTimer; // for compressing composite resets bool m_finishingCompositing; // finishCompositing() sets this variable while shutting down + ScreenLocker::ScreenLocker *m_screenLocker; + private: friend bool performTransiencyCheck(); }; @@ -1329,6 +1342,14 @@ inline bool Workspace::hasDecorationShadows() const return mgr->factory()->supports(AbilityProvidesShadow); } +inline Qt::Corner Workspace::decorationCloseButtonCorner() +{ + if (!hasDecorationPlugin()) { + return Qt::TopRightCorner; + } + return mgr->factory()->closeButtonCorner(); +} + inline bool Workspace::decorationHasAlpha() const { if (!hasDecorationPlugin()) {