2 * Copyright (C) 2013,2014,2015 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import AccountsService 0.1
19 import LightDM 0.1 as LightDM
20 import Ubuntu.Components 1.1
21 import Ubuntu.SystemImage 0.1
22 import Unity.Connectivity 0.1
23 import Unity.Launcher 0.1
24 import "../Components"
28 created: loader.status == Loader.Ready
30 property real dragHandleLeftMargin: 0
32 property url background
34 // How far to offset the top greeter layer during a launcher left-drag
35 property real launcherOffset
37 readonly property bool active: shown || hasLockedApp
38 readonly property bool fullyShown: loader.item ? loader.item.fullyShown : false
40 // True when the greeter is waiting for PAM or other setup process
41 readonly property alias waiting: d.waiting
43 property string lockedApp: ""
44 readonly property bool hasLockedApp: lockedApp !== ""
46 property bool forcedUnlock
47 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
49 property bool tabletMode
50 property url viewSource // only used for testing
52 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
53 property int failedLoginsDelayAttempts: 7 // number of failed logins
54 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
57 signal sessionStarted()
58 signal emergencyCall()
60 function forceShow() {
65 function notifyAppFocused(appId) {
71 if (appId === lockedApp) {
72 hide(); // show locked app
75 d.startUnlock(false /* toTheRight */);
77 } else if (appId !== "unity8-dash") { // dash isn't started by user
78 d.startUnlock(false /* toTheRight */);
82 function notifyAboutToFocusApp(appId) {
87 // A hint that we're about to focus an app. This way we can look
88 // a little more responsive, rather than waiting for the above
89 // notifyAppFocused call. We also need this in case we have a locked
90 // app, in order to show lockscreen instead of new app.
91 d.startUnlock(false /* toTheRight */);
94 // This is a just a glorified notifyAboutToFocusApp(), but it does one
95 // other thing: it hides any cover pages to the RIGHT, because the user
96 // just came from a launcher drag starting on the left.
97 // It also returns a boolean value, indicating whether there was a visual
98 // change or not (the shell only wants to hide the launcher if there was
100 function notifyShowingDashFromDrag() {
105 return d.startUnlock(true /* toTheRight */);
111 readonly property bool multiUser: LightDM.Users.count > 1
112 property int currentIndex
113 property int delayMinutes
114 property bool waiting
116 // We want 'launcherOffset' to animate down to zero. But not to animate
117 // while being dragged. So ideally we change this only when the user
118 // lets go and launcherOffset drops to zero. But we need to wait for
119 // the behavior to be enabled first. So we cache the last known good
120 // launcherOffset value to cover us during that brief gap between
121 // release and the behavior turning on.
122 property real lastKnownPositiveOffset // set in a launcherOffsetChanged below
123 property real launcherOffsetProxy: (shown && !launcherOffsetProxyBehavior.enabled) ? lastKnownPositiveOffset : 0
124 Behavior on launcherOffsetProxy {
125 id: launcherOffsetProxyBehavior
126 enabled: launcherOffset === 0
127 UbuntuNumberAnimation {}
130 function selectUser(uid, reset) {
136 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
137 AccountsService.user = user;
138 LauncherModel.setUser(user);
139 LightDM.Greeter.authenticate(user); // always resets auth state
144 if (LightDM.Greeter.startSessionSync()) {
146 loader.item.notifyAuthenticationSucceeded();
148 loader.item.notifyAuthenticationFailed();
153 function startUnlock(toTheRight) {
155 return loader.item.tryToUnlock(toTheRight);
162 onLauncherOffsetChanged: {
163 if (launcherOffset > 0) {
164 d.lastKnownPositiveOffset = launcherOffset;
168 onForcedUnlockChanged: {
169 if (forcedUnlock && shown) {
170 // pretend we were just authenticated
171 loader.item.notifyAuthenticationSucceeded();
182 Component.onCompleted: {
183 Connectivity.unlockAllModems();
190 if (d.delayMinutes > 0) {
192 if (d.delayMinutes > 0) {
200 // Nothing should leak to items behind the greeter
201 MouseArea { anchors.fill: parent }
209 active: root.required
210 source: root.viewSource.toString() ? root.viewSource :
211 (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml"
215 root.forceActiveFocus();
216 d.selectUser(d.currentIndex, true);
217 LightDM.Infographic.readyForDataChange();
223 d.selectUser(index, true);
227 LightDM.Greeter.respond(response);
229 if (LightDM.Greeter.active && !LightDM.Greeter.authenticated) { // could happen if forcedUnlock
235 onTease: root.tease()
236 onEmergencyCall: root.emergencyCall()
238 if (!loader.item.required) {
246 property: "backgroundTopMargin"
252 property: "launcherOffset"
253 value: d.launcherOffsetProxy
258 property: "dragHandleLeftMargin"
259 value: root.dragHandleLeftMargin
264 property: "delayMinutes"
265 value: d.delayMinutes
270 property: "background"
271 value: root.background
282 property: "alphanumeric"
283 value: AccountsService.passwordDisplayHint === AccountsService.Keyboard
288 property: "currentIndex"
289 value: d.currentIndex
294 property: "userModel"
300 property: "infographicModel"
301 value: LightDM.Infographic
306 target: LightDM.Greeter
308 onShowGreeter: root.forceShow()
316 if (!LightDM.Greeter.active) {
317 return; // could happen if hideGreeter() comes in before we prompt
320 // inefficient, but we only rarely deal with messages
321 var html = text.replace(/&/g, "&")
322 .replace(/</g, "<")
323 .replace(/>/g, ">")
324 .replace(/\n/g, "<br>");
326 html = "<font color=\"#df382c\">" + html + "</font>";
329 loader.item.showMessage(html);
335 if (!LightDM.Greeter.active) {
336 return; // could happen if hideGreeter() comes in before we prompt
339 loader.item.showPrompt(text, isSecret, isDefaultPrompt);
342 onAuthenticationComplete: {
345 if (LightDM.Greeter.authenticated) {
346 AccountsService.failedLogins = 0;
348 if (!LightDM.Greeter.promptless) {
352 if (!LightDM.Greeter.promptless) {
353 AccountsService.failedLogins++;
356 // Check if we should initiate a factory reset
357 if (maxFailedLogins >= 2) { // require at least a warning
358 if (AccountsService.failedLogins === maxFailedLogins - 1) {
359 loader.item.showLastChance();
360 } else if (AccountsService.failedLogins >= maxFailedLogins) {
361 SystemImage.factoryReset(); // Ouch!
365 // Check if we should initiate a forced login delay
366 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
367 d.delayMinutes = failedLoginsDelayMinutes;
368 forcedDelayTimer.start();
371 loader.item.notifyAuthenticationFailed();
372 if (!LightDM.Greeter.promptless) {
373 d.selectUser(d.currentIndex, false);
378 onRequestAuthenticationUser: {
379 // Find index for requested user, if it exists
380 for (var i = 0; i < LightDM.Users.count; i++) {
381 if (user === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
382 d.selectUser(i, true);
390 target: LightDM.Greeter
396 target: LightDM.Infographic
398 value: AccountsService.statsWelcomeScreen ? LightDM.Users.data(d.currentIndex, LightDM.UserRoles.NameRole) : ""
403 onLanguageChanged: LightDM.Infographic.readyForDataChange()