1 | /***************************************************************** |
2 | ksmserver - the KDE session management server |
3 | |
4 | Copyright 2000 Matthias Ettrich <ettrich@kde.org> |
5 | Copyright 2007 Urs Wolfer <uwolfer @ kde.org> |
6 | |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | of this software and associated documentation files (the "Software"), to deal |
9 | in the Software without restriction, including without limitation the rights |
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | copies of the Software, and to permit persons to whom the Software is |
12 | furnished to do so, subject to the following conditions: |
13 | |
14 | The above copyright notice and this permission notice shall be included in |
15 | all copies or substantial portions of the Software. |
16 | |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
21 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
23 | |
24 | ******************************************************************/ |
25 | |
26 | #include "shutdowndlg.h" |
27 | #include "plasma/framesvg.h" |
28 | #include "plasma/theme.h" |
29 | |
30 | #include <QApplication> |
31 | #include <QDesktopWidget> |
32 | #include <QTimer> |
33 | #include <QFile> |
34 | #include <QtDBus/QDBusConnection> |
35 | #include <QtDBus/QDBusMessage> |
36 | #include <QtDBus/QDBusPendingCall> |
37 | #include <QDeclarativeView> |
38 | #include <QDeclarativeContext> |
39 | #include <QDeclarativeEngine> |
40 | #include <QDeclarativePropertyMap> |
41 | |
42 | #include <kdialog.h> |
43 | #include <kiconloader.h> |
44 | #include <klocale.h> |
45 | #include <kuser.h> |
46 | #include <Solid/PowerManagement> |
47 | #include <kwindowsystem.h> |
48 | #include <netwm.h> |
49 | #include <KStandardDirs> |
50 | #include <kdeclarative.h> |
51 | |
52 | #include <stdio.h> |
53 | #include <kxerrorhandler.h> |
54 | |
55 | #include <kworkspace/kdisplaymanager.h> |
56 | |
57 | #include <config-workspace.h> |
58 | |
59 | #include "logouteffect.h" |
60 | #include "shutdowndlg.moc" |
61 | |
62 | #include <kjob.h> |
63 | |
64 | #define FONTCOLOR "#bfbfbf" |
65 | |
66 | KSMShutdownFeedback * KSMShutdownFeedback::s_pSelf = 0L; |
67 | |
68 | KSMShutdownFeedback::KSMShutdownFeedback() |
69 | : QWidget( 0L, Qt::Popup ), |
70 | m_currentY( 0 ), initialized( false ) |
71 | { |
72 | setObjectName( "feedbackwidget" ); |
73 | setAttribute( Qt::WA_NoSystemBackground ); |
74 | setAttribute( Qt::WA_PaintOnScreen ); |
75 | setGeometry( QApplication::desktop()->geometry() ); |
76 | m_pixmap = QPixmap( size() ); |
77 | QTimer::singleShot( 10, this, SLOT(slotPaintEffect()) ); |
78 | } |
79 | |
80 | |
81 | void KSMShutdownFeedback::paintEvent( QPaintEvent* ) |
82 | { |
83 | if ( !initialized ) |
84 | return; |
85 | |
86 | QPainter painter( this ); |
87 | painter.setCompositionMode( QPainter::CompositionMode_Source ); |
88 | painter.drawPixmap( 0, 0, m_pixmap ); |
89 | } |
90 | |
91 | void KSMShutdownFeedback::slotPaintEffect() |
92 | { |
93 | effect = LogoutEffect::create(this, &m_pixmap); |
94 | connect(effect, SIGNAL(initialized()), |
95 | this, SLOT (slotPaintEffectInitialized())); |
96 | |
97 | effect->start(); |
98 | } |
99 | |
100 | void KSMShutdownFeedback::slotPaintEffectInitialized() |
101 | { |
102 | initialized = true; |
103 | } |
104 | |
105 | void KSMShutdownFeedback::start() |
106 | { |
107 | if( KWindowSystem::compositingActive()) { |
108 | // HACK do properly |
109 | Display* dpy = QX11Info::display(); |
110 | char net_wm_cm_name[ 100 ]; |
111 | sprintf( net_wm_cm_name, "_NET_WM_CM_S%d" , DefaultScreen( dpy )); |
112 | Atom net_wm_cm = XInternAtom( dpy, net_wm_cm_name, False ); |
113 | Window sel = XGetSelectionOwner( dpy, net_wm_cm ); |
114 | Atom hack = XInternAtom( dpy, "_KWIN_LOGOUT_EFFECT" , False ); |
115 | bool wmsupport = false; |
116 | if( sel != None ) { |
117 | KXErrorHandler handler; |
118 | int cnt; |
119 | Atom* props = XListProperties( dpy, sel, &cnt ); |
120 | if( !handler.error( false ) && props != NULL && qFind( props, props + cnt, hack ) != props + cnt ) |
121 | wmsupport = true; |
122 | if( props != NULL ) |
123 | XFree( props ); |
124 | } |
125 | if( wmsupport ) { |
126 | // Announce that the user MAY be logging out (Intended for the compositor) |
127 | Atom announce = XInternAtom(dpy, "_KDE_LOGGING_OUT" , False); |
128 | unsigned char dummy = 0; |
129 | XChangeProperty(dpy, QX11Info::appRootWindow(), announce, announce, 8, PropModeReplace, |
130 | &dummy, 1); |
131 | |
132 | // Don't show our own effect |
133 | return; |
134 | } |
135 | } |
136 | s_pSelf = new KSMShutdownFeedback(); |
137 | s_pSelf->show(); |
138 | } |
139 | |
140 | void KSMShutdownFeedback::stop() |
141 | { |
142 | delete s_pSelf; |
143 | s_pSelf = NULL; |
144 | } |
145 | |
146 | void KSMShutdownFeedback::logoutCanceled() |
147 | { |
148 | if( KWindowSystem::compositingActive()) { |
149 | // We are no longer logging out, announce (Intended for the compositor) |
150 | Display* dpy = QX11Info::display(); |
151 | Atom announce = XInternAtom(dpy, "_KDE_LOGGING_OUT" , False); |
152 | XDeleteProperty(QX11Info::display(), QX11Info::appRootWindow(), announce); |
153 | } |
154 | } |
155 | |
156 | //////////// |
157 | |
158 | Q_DECLARE_METATYPE(Solid::PowerManagement::SleepState) |
159 | |
160 | KSMShutdownDlg::KSMShutdownDlg( QWidget* parent, |
161 | bool maysd, bool choose, KWorkSpace::ShutdownType sdtype, |
162 | const QString& theme) |
163 | : QDialog( parent, Qt::Popup ) //krazy:exclude=qclasses |
164 | // this is a WType_Popup on purpose. Do not change that! Not |
165 | // having a popup here has severe side effects. |
166 | { |
167 | winId(); // workaround for Qt4.3 setWindowRole() assert |
168 | setWindowRole( "logoutdialog" ); |
169 | //#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY)) |
170 | // Qt doesn't set this on unmanaged windows |
171 | QByteArray appName = qAppName().toLatin1(); |
172 | XClassHint class_hint; |
173 | class_hint.res_name = appName.data(); // application name |
174 | class_hint.res_class = const_cast<char *>(QX11Info::appClass()); // application class |
175 | XSetWMProperties( QX11Info::display(), winId(), NULL, NULL, NULL, 0, NULL, NULL, &class_hint ); |
176 | XChangeProperty( QX11Info::display(), winId(), |
177 | XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE" , False ), XA_STRING, 8, PropModeReplace, |
178 | (unsigned char *)"logoutdialog" , strlen( "logoutdialog" )); |
179 | |
180 | //#endif |
181 | |
182 | KDialog::centerOnScreen(this, -3); |
183 | |
184 | //kDebug() << "Creating QML view"; |
185 | m_view = new QDeclarativeView(this); |
186 | QDeclarativeContext *context = m_view->rootContext(); |
187 | context->setContextProperty("maysd" , maysd); |
188 | context->setContextProperty("choose" , choose); |
189 | context->setContextProperty("sdtype" , sdtype); |
190 | |
191 | QDeclarativePropertyMap *mapShutdownType = new QDeclarativePropertyMap(this); |
192 | mapShutdownType->insert("ShutdownTypeDefault" , QVariant::fromValue((int)KWorkSpace::ShutdownTypeDefault)); |
193 | mapShutdownType->insert("ShutdownTypeNone" , QVariant::fromValue((int)KWorkSpace::ShutdownTypeNone)); |
194 | mapShutdownType->insert("ShutdownTypeReboot" , QVariant::fromValue((int)KWorkSpace::ShutdownTypeReboot)); |
195 | mapShutdownType->insert("ShutdownTypeHalt" , QVariant::fromValue((int)KWorkSpace::ShutdownTypeHalt)); |
196 | mapShutdownType->insert("ShutdownTypeLogout" , QVariant::fromValue((int)KWorkSpace::ShutdownTypeLogout)); |
197 | context->setContextProperty("ShutdownType" , mapShutdownType); |
198 | |
199 | QDeclarativePropertyMap *mapSpdMethods = new QDeclarativePropertyMap(this); |
200 | QSet<Solid::PowerManagement::SleepState> spdMethods = Solid::PowerManagement::supportedSleepStates(); |
201 | mapSpdMethods->insert("StandbyState" , QVariant::fromValue(spdMethods.contains(Solid::PowerManagement::StandbyState))); |
202 | mapSpdMethods->insert("SuspendState" , QVariant::fromValue(spdMethods.contains(Solid::PowerManagement::SuspendState))); |
203 | mapSpdMethods->insert("HibernateState" , QVariant::fromValue(spdMethods.contains(Solid::PowerManagement::HibernateState))); |
204 | context->setContextProperty("spdMethods" , mapSpdMethods); |
205 | |
206 | QString bootManager = KConfig(KDE_CONFDIR "/kdm/kdmrc" , KConfig::SimpleConfig).group("Shutdown" ).readEntry("BootManager" , "None" ); |
207 | context->setContextProperty("bootManager" , bootManager); |
208 | |
209 | QStringList options; |
210 | int def, cur; |
211 | if ( KDisplayManager().bootOptions( rebootOptions, def, cur ) ) { |
212 | if ( cur > -1 ) { |
213 | def = cur; |
214 | } |
215 | } |
216 | QDeclarativePropertyMap *rebootOptionsMap = new QDeclarativePropertyMap(this); |
217 | rebootOptionsMap->insert("options" , QVariant::fromValue(rebootOptions)); |
218 | rebootOptionsMap->insert("default" , QVariant::fromValue(def)); |
219 | context->setContextProperty("rebootOptions" , rebootOptionsMap); |
220 | |
221 | setModal( true ); |
222 | |
223 | // window stuff |
224 | m_view->setFrameShape(QFrame::NoFrame); |
225 | m_view->setWindowFlags(Qt::X11BypassWindowManagerHint); |
226 | m_view->setAttribute(Qt::WA_TranslucentBackground); |
227 | setAttribute(Qt::WA_TranslucentBackground); |
228 | setStyleSheet("background:transparent;" ); |
229 | QPalette pal = m_view->palette(); |
230 | pal.setColor(backgroundRole(), Qt::transparent); |
231 | m_view->setPalette(pal); |
232 | m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
233 | m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
234 | // engine stuff |
235 | KDeclarative kdeclarative; |
236 | kdeclarative.setDeclarativeEngine(m_view->engine()); |
237 | kdeclarative.initialize(); |
238 | kdeclarative.setupBindings(); |
239 | m_view->installEventFilter(this); |
240 | |
241 | QString fileName = KStandardDirs::locate("data" , QString("ksmserver/themes/%1/main.qml" ).arg(theme)); |
242 | if (QFile::exists(fileName)) { |
243 | //kDebug() << "Using QML theme" << fileName; |
244 | m_view->setSource(QUrl::fromLocalFile(fileName)); |
245 | } |
246 | QGraphicsObject *rootObject = m_view->rootObject(); |
247 | connect(rootObject, SIGNAL(logoutRequested()), SLOT(slotLogout())); |
248 | connect(rootObject, SIGNAL(haltRequested()), SLOT(slotHalt())); |
249 | connect(rootObject, SIGNAL(suspendRequested(int)), SLOT(slotSuspend(int)) ); |
250 | connect(rootObject, SIGNAL(rebootRequested()), SLOT(slotReboot())); |
251 | connect(rootObject, SIGNAL(rebootRequested2(int)), SLOT(slotReboot(int)) ); |
252 | connect(rootObject, SIGNAL(cancelRequested()), SLOT(reject())); |
253 | connect(rootObject, SIGNAL(lockScreenRequested()), SLOT(slotLockScreen())); |
254 | m_view->show(); |
255 | m_view->setFocus(); |
256 | adjustSize(); |
257 | } |
258 | |
259 | bool KSMShutdownDlg::eventFilter ( QObject * watched, QEvent * event ) |
260 | { |
261 | if (watched == m_view && event->type() == QEvent::Resize) { |
262 | adjustSize(); |
263 | } |
264 | return QDialog::eventFilter(watched, event); |
265 | } |
266 | |
267 | void KSMShutdownDlg::resizeEvent(QResizeEvent *e) |
268 | { |
269 | QDialog::resizeEvent( e ); |
270 | |
271 | if( KWindowSystem::compositingActive()) { |
272 | clearMask(); |
273 | } else { |
274 | setMask(m_view->mask()); |
275 | } |
276 | |
277 | KDialog::centerOnScreen(this, -3); |
278 | } |
279 | |
280 | void KSMShutdownDlg::slotLogout() |
281 | { |
282 | m_shutdownType = KWorkSpace::ShutdownTypeNone; |
283 | accept(); |
284 | } |
285 | |
286 | void KSMShutdownDlg::slotReboot() |
287 | { |
288 | // no boot option selected -> current |
289 | m_bootOption.clear(); |
290 | m_shutdownType = KWorkSpace::ShutdownTypeReboot; |
291 | accept(); |
292 | } |
293 | |
294 | void KSMShutdownDlg::slotReboot(int opt) |
295 | { |
296 | if (int(rebootOptions.size()) > opt) |
297 | m_bootOption = rebootOptions[opt]; |
298 | m_shutdownType = KWorkSpace::ShutdownTypeReboot; |
299 | accept(); |
300 | } |
301 | |
302 | |
303 | void KSMShutdownDlg::slotLockScreen() |
304 | { |
305 | m_bootOption.clear(); |
306 | QDBusMessage call = QDBusMessage::createMethodCall("org.kde.screensaver" , |
307 | "/ScreenSaver" , |
308 | "org.freedesktop.ScreenSaver" , |
309 | "Lock" ); |
310 | QDBusConnection::sessionBus().asyncCall(call); |
311 | reject(); |
312 | } |
313 | |
314 | void KSMShutdownDlg::slotHalt() |
315 | { |
316 | m_bootOption.clear(); |
317 | m_shutdownType = KWorkSpace::ShutdownTypeHalt; |
318 | accept(); |
319 | } |
320 | |
321 | |
322 | void KSMShutdownDlg::slotSuspend(int spdMethod) |
323 | { |
324 | m_bootOption.clear(); |
325 | switch (spdMethod) { |
326 | case Solid::PowerManagement::StandbyState: |
327 | case Solid::PowerManagement::SuspendState: |
328 | Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState, 0, 0); |
329 | break; |
330 | case Solid::PowerManagement::HibernateState: |
331 | Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState, 0, 0); |
332 | break; |
333 | } |
334 | reject(); |
335 | } |
336 | |
337 | bool KSMShutdownDlg::confirmShutdown( |
338 | bool maysd, bool choose, KWorkSpace::ShutdownType& sdtype, QString& bootOption, |
339 | const QString& theme) |
340 | { |
341 | KSMShutdownDlg* l = new KSMShutdownDlg( 0, |
342 | //KSMShutdownFeedback::self(), |
343 | maysd, choose, sdtype, theme ); |
344 | XClassHint classHint; |
345 | classHint.res_name = const_cast<char*>("ksmserver" ); |
346 | classHint.res_class = const_cast<char*>("ksmserver" ); |
347 | |
348 | XSetClassHint(QX11Info::display(), l->winId(), &classHint); |
349 | bool result = l->exec(); |
350 | sdtype = l->m_shutdownType; |
351 | bootOption = l->m_bootOption; |
352 | |
353 | delete l; |
354 | |
355 | return result; |
356 | } |
357 | |