2 * Copyright (C) 2013-2016 Canonical Ltd.
3 * Copyright (C) 2019-2021 UBports Foundation
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import QtQuick.Window 2.2
20 import AccountsService 0.1
21 import QtMir.Application 0.1
22 import Lomiri.Components 1.3
23 import Lomiri.Components.Popups 1.3
24 import Lomiri.Gestures 0.1
25 import Lomiri.Telephony 0.1 as Telephony
26 import Lomiri.ModemConnectivity 0.1
27 import Lomiri.Launcher 0.1
28 import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
32 import SessionBroadcast 0.1
37 import "Notifications"
41 import "Components/PanelState"
42 import Lomiri.Notifications 1.0 as NotificationBackend
43 import Lomiri.Session 0.1
44 import Lomiri.Indicators 0.1 as Indicators
46 import WindowManager 1.0
52 theme.name: "Lomiri.Components.Themes.SuruDark"
54 // to be set from outside
55 property int orientationAngle: 0
56 property int orientation
57 property Orientations orientations
58 property real nativeWidth
59 property real nativeHeight
60 property alias panelAreaShowProgress: panel.panelAreaShowProgress
61 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 stage.updateFocusedAppOrientation();
67 function updateFocusedAppOrientationAnimated() {
68 stage.updateFocusedAppOrientationAnimated();
70 property bool hasMouse: false
71 property bool hasKeyboard: false
72 property bool hasTouchscreen: false
73 property bool supportsMultiColorLed: true
75 // The largest dimension, in pixels, of all of the screens this Shell is
77 // If a script sets the shell to 240x320 when it was 320x240, we could
78 // end up in a situation where our dimensions are 240x240 for a short time.
79 // Notifying the Wallpaper of both events would make it reload the image
80 // twice. So, we use a Binding { delayed: true }.
81 property real largestScreenDimension
85 property: "largestScreenDimension"
86 value: Math.max(nativeWidth, nativeHeight)
90 property alias lightIndicators: indicatorsModel.light
92 // to be read from outside
93 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
95 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
96 && stage.orientationChangesEnabled
97 && (!greeter || !greeter.animating)
99 readonly property bool showingGreeter: greeter && greeter.shown
101 property bool startingUp: true
102 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
104 property int supportedOrientations: {
106 // Ensure we don't rotate during start up
107 return Qt.PrimaryOrientation;
108 } else if (showingGreeter || notifications.topmostIsFullscreen) {
109 return Qt.PrimaryOrientation;
111 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
115 readonly property var mainApp: stage.mainApp
117 readonly property var topLevelSurfaceList: {
118 if (!WMScreen.currentWorkspace) return null;
119 return stage.temporarySelectedWorkspace ? stage.temporarySelectedWorkspace.windowModel : WMScreen.currentWorkspace.windowModel
123 _onMainAppChanged((mainApp ? mainApp.appId : ""));
126 target: ApplicationManager
128 if (shell.mainApp && shell.mainApp.appId === appId) {
129 _onMainAppChanged(appId);
134 // Calls attention back to the most important thing that's been focused
135 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
136 // goes over everything if it is locked)
137 // Must be called whenever app focus changes occur, even if the focus change
138 // is "nothing is focused". In that case, call with appId = ""
139 function _onMainAppChanged(appId) {
143 // If this happens on first boot, we may be in the
144 // wizard while receiving a call. A call is more
145 // important than the wizard so just bail out of it.
149 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
150 // If we are in the middle of a call, make dialer lockedApp. The
151 // Greeter will show it when it's notified of the focus.
152 // This can happen if user backs out of dialer back to greeter, then
153 // launches dialer again.
154 greeter.lockedApp = appId;
157 panel.indicators.hide();
158 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
161 // *Always* make sure the greeter knows that the focused app changed
162 if (greeter) greeter.notifyAppFocusRequested(appId);
165 // For autopilot consumption
166 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
168 // Note when greeter is waiting on PAM, so that we can disable edges until
169 // we know which user data to show and whether the session is locked.
170 readonly property bool waitingOnGreeter: greeter && greeter.waiting
172 // True when the user is logged in with no apps running
173 readonly property bool atDesktop: topLevelSurfaceList && greeter && topLevelSurfaceList.count === 0 && !greeter.active
175 onAtDesktopChanged: {
176 if (atDesktop && stage) {
181 property real edgeSize: units.gu(settings.edgeDragWidth)
184 id: wallpaperResolver
185 objectName: "wallpaperResolver"
187 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
188 readonly property bool hasCustomBackground: background != defaultBackground
191 id: backgroundSettings
192 schema.id: "org.gnome.desktop.background"
196 AccountsService.backgroundFile,
197 backgroundSettings.pictureUri,
202 readonly property alias greeter: greeterLoader.item
204 function activateApplication(appId) {
205 topLevelSurfaceList.pendingActivation();
207 // Either open the app in our own session, or -- if we're acting as a
208 // greeter -- ask the user's session to open it for us.
209 if (shell.mode === "greeter") {
210 activateURL("application:///" + appId + ".desktop");
217 function activateURL(url) {
218 SessionBroadcast.requestUrlStart(AccountsService.user, url);
219 greeter.notifyUserRequestedApp();
220 panel.indicators.hide();
223 function startApp(appId) {
224 if (!ApplicationManager.findApplication(appId)) {
225 ApplicationManager.startApplication(appId);
227 ApplicationManager.requestFocusApplication(appId);
231 function startLockedApp(app) {
232 topLevelSurfaceList.pendingActivation();
234 if (greeter.locked) {
235 greeter.lockedApp = app;
237 startApp(app); // locked apps are always in our same session
241 target: LauncherModel
242 property: "applicationManager"
243 value: ApplicationManager
246 Component.onCompleted: {
247 finishStartUpTimer.start();
255 id: physicalKeysMapper
256 objectName: "physicalKeysMapper"
258 onPowerKeyLongPressed: dialogs.showPowerDialog();
259 onVolumeDownTriggered: volumeControl.volumeDown();
260 onVolumeUpTriggered: volumeControl.volumeUp();
261 onScreenshotTriggered: itemGrabber.capture(shell);
265 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
270 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
271 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
275 objectName: "windowInputMonitor"
276 onHomeKeyActivated: {
277 // Ignore when greeter is active, to avoid pocket presses
278 if (!greeter.active) {
279 launcher.toggleDrawer(/* focusInputField */ false,
280 /* onlyOpen */ false,
281 /* alsoToggleLauncher */ true);
284 onTouchBegun: { cursor.opacity = 0; }
286 // move the (hidden) cursor to the last known touch position
287 var mappedCoords = mapFromItem(null, pos.x, pos.y);
288 cursor.x = mappedCoords.x;
289 cursor.y = mappedCoords.y;
290 cursor.mouseNeverMoved = false;
294 AvailableDesktopArea {
295 id: availableDesktopAreaItem
297 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
298 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
303 schema.id: "com.lomiri.Shell"
308 objectName: "panelState"
315 height: parent.height
323 dragAreaWidth: shell.edgeSize
324 background: wallpaperResolver.background
325 backgroundSourceSize: shell.largestScreenDimension
327 applicationManager: ApplicationManager
328 topLevelSurfaceList: shell.topLevelSurfaceList
329 inputMethodRect: inputMethod.visibleRect
330 rightEdgePushProgress: rightEdgeBarrier.progress
331 availableDesktopArea: availableDesktopAreaItem
332 launcherLeftMargin: launcher.visibleWidth
334 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
336 : shell.usageScenario
338 mode: usageScenario == "phone" ? "staged"
339 : usageScenario == "tablet" ? "stagedWithSideStage"
342 shellOrientation: shell.orientation
343 shellOrientationAngle: shell.orientationAngle
344 orientations: shell.orientations
345 nativeWidth: shell.nativeWidth
346 nativeHeight: shell.nativeHeight
348 allowInteractivity: (!greeter || !greeter.shown)
349 && panel.indicators.fullyClosed
350 && !notifications.useModal
351 && !launcher.takesFocus
353 suspended: greeter.shown
354 altTabPressed: physicalKeysMapper.altTabPressed
355 oskEnabled: shell.oskEnabled
356 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
357 panelState: panelState
359 onSpreadShownChanged: {
360 panel.indicators.hide();
361 panel.applicationMenus.hide();
368 minimumTouchPoints: 4
369 maximumTouchPoints: minimumTouchPoints
371 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
372 touchPoints.length >= minimumTouchPoints &&
373 touchPoints.length <= maximumTouchPoints
374 property bool wasPressed: false
376 onRecognisedPressChanged: {
377 if (recognisedPress) {
383 if (status !== TouchGestureArea.Recognized) {
384 if (status === TouchGestureArea.WaitingForTouch) {
385 if (wasPressed && !dragging) {
386 launcher.toggleDrawer(true);
397 objectName: "inputMethod"
400 topMargin: panel.panelHeight
401 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
403 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
408 objectName: "greeterLoader"
411 if (shell.mode != "shell") {
412 if (screenWindow.primary) return integratedGreeter;
413 return secondaryGreeter;
415 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
418 item.objectName = "greeter"
420 property bool toggleDrawerAfterUnlock: false
427 // Show drawer in case showHome() requests it
428 if (greeterLoader.toggleDrawerAfterUnlock) {
429 launcher.toggleDrawer(false);
430 greeterLoader.toggleDrawerAfterUnlock = false;
439 id: integratedGreeter
442 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
443 hides: [launcher, panel.indicators, panel.applicationMenus]
444 tabletMode: shell.usageScenario != "phone"
445 forcedUnlock: wizard.active || shell.mode === "full-shell"
446 background: wallpaperResolver.background
447 backgroundSourceSize: shell.largestScreenDimension
448 hasCustomBackground: wallpaperResolver.hasCustomBackground
449 inputMethodRect: inputMethod.visibleRect
450 hasKeyboard: shell.hasKeyboard
451 allowFingerprint: !dialogs.hasActiveDialog &&
452 !notifications.topmostIsFullscreen &&
453 !panel.indicators.shown
454 panelHeight: panel.panelHeight
456 // avoid overlapping with Launcher's edge drag area
457 // FIXME: Fix TouchRegistry & friends and remove this workaround
458 // Issue involves launcher's DDA getting disabled on a long
460 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
463 if (!tutorial.running) {
468 onEmergencyCall: startLockedApp("dialer-app")
475 hides: [launcher, panel.indicators]
480 // See powerConnection for why this is useful
481 id: showGreeterDelayed
484 // Go through the dbus service, because it has checks for whether
485 // we are even allowed to lock or not.
486 DBusLomiriSessionService.PromptLock();
495 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
496 // We just received an incoming call while locked. The
497 // indicator will have already launched dialer-app for us, but
498 // there is a race between "hasCalls" changing and the dialer
499 // starting up. So in case we lose that race, we'll start/
500 // focus the dialer ourselves here too. Even if the indicator
501 // didn't launch the dialer for some reason (or maybe a call
502 // started via some other means), if an active call is
503 // happening, we want to be in the dialer.
504 startLockedApp("dialer-app")
514 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
515 !callManager.hasCalls && !wizard.active) {
516 // We don't want to simply call greeter.showNow() here, because
517 // that will take too long. Qt will delay button event
518 // handling until the greeter is done loading and may think the
519 // user held down the power button the whole time, leading to a
520 // power dialog being shown. Instead, delay showing the
521 // greeter until we've finished handling the event. We could
522 // make the greeter load asynchronously instead, but that
523 // introduces a whole host of timing issues, especially with
524 // its animations. So this is simpler.
525 showGreeterDelayed.start();
530 function showHome() {
531 greeter.notifyUserRequestedApp();
533 if (shell.mode === "greeter") {
534 SessionBroadcast.requestHomeShown(AccountsService.user);
536 if (!greeter.active) {
537 launcher.toggleDrawer(false);
539 greeterLoader.toggleDrawerAfterUnlock = true;
553 anchors.fill: parent //because this draws indicator menus
555 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
556 minimizedPanelHeight: units.gu(3)
557 expandedPanelHeight: units.gu(7)
558 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
562 available: tutorial.panelEnabled
563 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
564 && (!greeter || !greeter.hasLockedApp)
565 && !shell.waitingOnGreeter
566 && settings.enableIndicatorMenu
568 model: Indicators.IndicatorsModel {
570 // tablet and phone both use the same profile
571 // FIXME: use just "phone" for greeter too, but first fix
572 // greeter app launching to either load the app inside the
573 // greeter or tell the session to load the app. This will
574 // involve taking the url-dispatcher dbus name and using
575 // SessionBroadcast to tell the session.
576 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
577 Component.onCompleted: {
585 available: (!greeter || !greeter.shown)
586 && !shell.waitingOnGreeter
587 && !stage.spreadShown
590 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
591 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
593 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
594 || greeter.hasLockedApp
595 greeterShown: greeter && greeter.shown
596 hasKeyboard: shell.hasKeyboard
597 panelState: panelState
598 supportsMultiColorLed: shell.supportsMultiColorLed
603 objectName: "launcher"
605 anchors.top: parent.top
606 anchors.topMargin: inverted ? 0 : panel.panelHeight
607 anchors.bottom: parent.bottom
609 dragAreaWidth: shell.edgeSize
610 available: tutorial.launcherEnabled
611 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
612 && !greeter.hasLockedApp
613 && !shell.waitingOnGreeter
614 inverted: shell.usageScenario !== "desktop"
615 superPressed: physicalKeysMapper.superPressed
616 superTabPressed: physicalKeysMapper.superTabPressed
617 panelWidth: units.gu(settings.launcherWidth)
618 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
619 topPanelHeight: panel.panelHeight
620 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
621 privateMode: greeter.active
622 background: wallpaperResolver.background
623 backgroundSourceSize: shell.largestScreenDimension
625 // It can be assumed that the Launcher and Panel would overlap if
626 // the Panel is open and taking up the full width of the shell
627 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
629 // The "autohideLauncher" setting is only valid in desktop mode
630 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
632 // The Launcher should absolutely not be locked visible under some
634 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
636 onShowDashHome: showHome()
637 onLauncherApplicationSelected: {
638 greeter.notifyUserRequestedApp();
639 shell.activateApplication(appId);
643 panel.indicators.hide();
644 panel.applicationMenus.hide();
647 onDrawerShownChanged: {
649 panel.indicators.hide();
650 panel.applicationMenus.hide();
660 shortcut: Qt.MetaModifier | Qt.Key_A
662 launcher.toggleDrawer(true);
666 shortcut: Qt.AltModifier | Qt.Key_F1
668 launcher.openForKeyboardNavigation();
672 shortcut: Qt.MetaModifier | Qt.Key_0
674 if (LauncherModel.get(9)) {
675 activateApplication(LauncherModel.get(9).appId);
682 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
684 if (LauncherModel.get(index)) {
685 activateApplication(LauncherModel.get(index).appId);
692 KeyboardShortcutsOverlay {
693 objectName: "shortcutsOverlay"
694 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
695 && height < parent.height - padding - panel.panelHeight
696 anchors.centerIn: parent
697 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
698 anchors.verticalCenterOffset: panel.panelHeight/2
700 opacity: enabled ? 0.95 : 0
702 Behavior on opacity {
703 LomiriNumberAnimation {}
709 objectName: "tutorial"
712 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
713 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
714 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
715 inputMethod.visible ||
716 (launcher.shown && !launcher.lockedVisible) ||
717 panel.indicators.shown || stage.rightEdgeDragProgress > 0
718 usageScenario: shell.usageScenario
719 lastInputTimestamp: inputFilter.lastInputTimestamp
729 deferred: shell.mode === "greeter"
731 function unlockWhenDoneWithWizard() {
733 ModemConnectivity.unlockAllModems();
737 Component.onCompleted: unlockWhenDoneWithWizard()
738 onActiveChanged: unlockWhenDoneWithWizard()
741 MouseArea { // modal notifications prevent interacting with other contents
743 visible: notifications.useModal
750 model: NotificationBackend.Model
752 hasMouse: shell.hasMouse
753 background: wallpaperResolver.background
755 y: topmostIsFullscreen ? 0 : panel.panelHeight
756 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
761 when: overlay.width <= units.gu(60)
763 target: notifications
764 anchors.left: parent.left
765 anchors.right: parent.right
770 when: overlay.width > units.gu(60)
772 target: notifications
773 anchors.left: undefined
774 anchors.right: parent.right
776 PropertyChanges { target: notifications; width: units.gu(38) }
783 enabled: !greeter.shown
785 // NB: it does its own positioning according to the specified edge
789 panel.indicators.hide()
792 material: Component {
798 anchors.centerIn: parent
800 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
801 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
811 objectName: "dialogs"
813 visible: hasActiveDialog
815 usageScenario: shell.usageScenario
816 hasKeyboard: shell.hasKeyboard
818 shutdownFadeOutRectangle.enabled = true;
819 shutdownFadeOutRectangle.visible = true;
820 shutdownFadeOut.start();
825 target: SessionBroadcast
826 onShowHome: if (shell.mode !== "greeter") showHome()
831 objectName: "urlDispatcher"
832 active: shell.mode === "greeter"
833 onUrlRequested: shell.activateURL(url)
840 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
843 ignoreUnknownSignals: true
844 onItemSnapshotRequested: itemGrabber.capture(item)
849 id: cursorHidingTimer
851 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
852 onTriggered: cursor.opacity = 0;
860 topBoundaryOffset: panel.panelHeight
861 enabled: shell.hasMouse && screenWindow.active
864 property bool mouseNeverMoved: true
866 target: cursor; property: "x"; value: shell.width / 2
867 when: cursor.mouseNeverMoved && cursor.visible
870 target: cursor; property: "y"; value: shell.height / 2
871 when: cursor.mouseNeverMoved && cursor.visible
874 confiningItem: stage.itemConfiningMouseCursor
878 readonly property var previewRectangle: stage.previewRectangle.target &&
879 stage.previewRectangle.target.dragging ?
880 stage.previewRectangle : null
882 onPushedLeftBoundary: {
883 if (buttons === Qt.NoButton) {
884 launcher.pushEdge(amount);
885 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
886 previewRectangle.maximizeLeft(amount);
890 onPushedRightBoundary: {
891 if (buttons === Qt.NoButton) {
892 rightEdgeBarrier.push(amount);
893 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
894 previewRectangle.maximizeRight(amount);
898 onPushedTopBoundary: {
899 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
900 previewRectangle.maximize(amount);
903 onPushedTopLeftCorner: {
904 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
905 previewRectangle.maximizeTopLeft(amount);
908 onPushedTopRightCorner: {
909 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
910 previewRectangle.maximizeTopRight(amount);
913 onPushedBottomLeftCorner: {
914 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
915 previewRectangle.maximizeBottomLeft(amount);
918 onPushedBottomRightCorner: {
919 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
920 previewRectangle.maximizeBottomRight(amount);
924 if (previewRectangle) {
925 previewRectangle.stop();
930 mouseNeverMoved = false;
934 Behavior on opacity { LomiriNumberAnimation {} }
937 // non-visual objects
939 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
944 id: shutdownFadeOutRectangle
951 NumberAnimation on opacity {
956 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
957 DBusLomiriSessionService.shutdown();