Compare commits
	
		
			6 Commits
		
	
	
		
			wip/smcv/i
			...
			wip/screen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 352ad8f558 | ||
|   | d5dc886748 | ||
|   | 17044adf96 | ||
|   | 9dfd805be2 | ||
|   | bbbb6b685f | ||
|   | 71c0d5f82d | 
| @@ -1715,7 +1715,7 @@ StScrollBar StButton#vhandle:hover | ||||
| } | ||||
|  | ||||
| .lightbox { | ||||
|     background-color: rgba(0, 0, 0, 0.4); | ||||
|     background-color: black; | ||||
| } | ||||
|  | ||||
| .flashspot { | ||||
|   | ||||
| @@ -78,6 +78,7 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/popupMenu.js		\ | ||||
| 	ui/remoteSearch.js	\ | ||||
| 	ui/runDialog.js		\ | ||||
|         ui/screenShield.js	\ | ||||
| 	ui/scripting.js		\ | ||||
| 	ui/search.js		\ | ||||
| 	ui/searchDisplay.js	\ | ||||
| @@ -91,6 +92,7 @@ nobase_dist_js_DATA = 	\ | ||||
| 	ui/status/bluetooth.js	\ | ||||
| 	ui/telepathyClient.js	\ | ||||
| 	ui/tweener.js		\ | ||||
| 	ui/unlockDialog.js	\ | ||||
| 	ui/userMenu.js		\ | ||||
| 	ui/viewSelector.js	\ | ||||
| 	ui/wanda.js		\ | ||||
|   | ||||
| @@ -39,8 +39,8 @@ const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const _PASSWORD_SERVICE_NAME = 'gdm-password'; | ||||
| const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; | ||||
| const PASSWORD_SERVICE_NAME = 'gdm-password'; | ||||
| const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; | ||||
| const _FADE_ANIMATION_TIME = 0.16; | ||||
| const _RESIZE_ANIMATION_TIME = 0.25; | ||||
| const _SCROLL_ANIMATION_TIME = 2.0; | ||||
| @@ -747,15 +747,13 @@ const LoginDialog = new Lang.Class({ | ||||
|                      Lang.bind(this, this._onOpened)); | ||||
|  | ||||
|         this._userManager = AccountsService.UserManager.get_default() | ||||
|         this._greeterClient = new GdmGreeter.Client(); | ||||
|         this._greeterClient = GdmGreeter.Server.new_for_greeter_sync(null); | ||||
|  | ||||
|         this._greeterClient.open_connection(); | ||||
|  | ||||
|         this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME); | ||||
|         this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null); | ||||
|  | ||||
|         this._greeterClient.connect('reset', | ||||
|                                     Lang.bind(this, this._onReset)); | ||||
|         this._greeterClient.connect('default-session-changed', | ||||
|         this._greeterClient.connect('default-session-name-changed', | ||||
|                                     Lang.bind(this, this._onDefaultSessionChanged)); | ||||
|         this._greeterClient.connect('info', | ||||
|                                     Lang.bind(this, this._onInfo)); | ||||
| @@ -769,8 +767,6 @@ const LoginDialog = new Lang.Class({ | ||||
|                                     Lang.bind(this, this._onSessionOpened)); | ||||
|         this._greeterClient.connect('timed-login-requested', | ||||
|                                     Lang.bind(this, this._onTimedLoginRequested)); | ||||
|         this._greeterClient.connect('authentication-failed', | ||||
|                                     Lang.bind(this, this._onAuthenticationFailed)); | ||||
|         this._greeterClient.connect('conversation-stopped', | ||||
|                                     Lang.bind(this, this._onConversationStopped)); | ||||
|  | ||||
| @@ -899,7 +895,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                     this._haveFingerprintReader = true; | ||||
|  | ||||
|                 if (this._haveFingerprintReader) | ||||
|                     this._greeterClient.call_start_conversation(_FINGERPRINT_SERVICE_NAME); | ||||
|                     this._greeterClient.call_start_conversation_sync(FINGERPRINT_SERVICE_NAME, null); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
| @@ -918,7 +914,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onReset: function(client, serviceName) { | ||||
|         this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME); | ||||
|         this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null); | ||||
|         this._startFingerprintConversationIfNeeded(); | ||||
|  | ||||
|         let tasks = [this._hidePrompt, | ||||
| @@ -954,7 +950,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         // We don't display fingerprint messages, because they | ||||
|         // have words like UPEK in them. Instead we use the messages | ||||
|         // as a cue to display our own message. | ||||
|         if (serviceName == _FINGERPRINT_SERVICE_NAME && | ||||
|         if (serviceName == FINGERPRINT_SERVICE_NAME && | ||||
|             this._haveFingerprintReader && | ||||
|             (!this._promptFingerprintMessage.visible || | ||||
|              this._promptFingerprintMessage.opacity != 255)) { | ||||
| @@ -963,7 +959,7 @@ const LoginDialog = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (serviceName != _PASSWORD_SERVICE_NAME) | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notifyError(info); | ||||
|     }, | ||||
| @@ -971,13 +967,13 @@ const LoginDialog = new Lang.Class({ | ||||
|     _onProblem: function(client, serviceName, problem) { | ||||
|         // we don't want to show auth failed messages to | ||||
|         // users who haven't enrolled their fingerprint. | ||||
|         if (serviceName != _PASSWORD_SERVICE_NAME) | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notifyError(problem); | ||||
|     }, | ||||
|  | ||||
|     _onCancel: function(client) { | ||||
|         this._greeterClient.call_cancel(); | ||||
|         this._greeterClient.call_cancel_sync(null); | ||||
|     }, | ||||
|  | ||||
|     _fadeInPrompt: function() { | ||||
| @@ -1084,7 +1080,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                          let _text = this._promptEntry.get_text(); | ||||
|                          this._promptEntry.reactive = false; | ||||
|                          this._promptEntry.add_style_pseudo_class('insensitive'); | ||||
|                          this._greeterClient.call_answer_query(serviceName, _text); | ||||
|                          this._greeterClient.call_answer_query_sync(serviceName, _text, null); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -1092,7 +1088,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
|         // We only expect questions to come from the main auth service | ||||
|         if (serviceName != _PASSWORD_SERVICE_NAME) | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptEntry.set_text(''); | ||||
| @@ -1102,7 +1098,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|     _onSecretInfoQuery: function(client, serviceName, secretQuestion) { | ||||
|         // We only expect secret requests to come from the main auth service | ||||
|         if (serviceName != _PASSWORD_SERVICE_NAME) | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptEntry.set_text(''); | ||||
| @@ -1111,7 +1107,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onSessionOpened: function(client, serviceName) { | ||||
|         this._greeterClient.call_start_session_when_ready(serviceName, true); | ||||
|         this._greeterClient.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|     }, | ||||
|  | ||||
|     _waitForItemForUser: function(userName) { | ||||
| @@ -1193,7 +1189,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      function() { | ||||
|                          this._timedLoginBatch = null; | ||||
|                          this._greeterClient.call_begin_auto_login(userName); | ||||
|                          this._greeterClient.call_begin_auto_login_sync(userName, null); | ||||
|                      }]; | ||||
|  | ||||
|         this._timedLoginBatch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -1236,17 +1232,13 @@ const LoginDialog = new Lang.Class({ | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     _onAuthenticationFailed: function(client) { | ||||
|         this._greeterClient.call_cancel(); | ||||
|     }, | ||||
|  | ||||
|     _onConversationStopped: function(client, serviceName) { | ||||
|         // if the password service fails, then cancel everything. | ||||
|         // But if, e.g., fingerprint fails, still give | ||||
|         // password authentication a chance to succeed | ||||
|         if (serviceName == _PASSWORD_SERVICE_NAME) { | ||||
|             this._greeterClient.call_cancel(); | ||||
|         } else if (serviceName == _FINGERPRINT_SERVICE_NAME) { | ||||
|         if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|             this._greeterClient.call_cancel_sync(null); | ||||
|         } else if (serviceName == FINGERPRINT_SERVICE_NAME) { | ||||
|             _fadeOutActor(this._promptFingerprintMessage); | ||||
|         } | ||||
|     }, | ||||
| @@ -1269,7 +1261,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                       this._fadeOutLogo]), | ||||
|  | ||||
|                      function() { | ||||
|                          this._greeterClient.call_begin_verification(_PASSWORD_SERVICE_NAME); | ||||
|                          this._greeterClient.call_begin_verification_sync(PASSWORD_SERVICE_NAME, null); | ||||
|                      }]; | ||||
|  | ||||
|         let batch = new Batch.ConsecutiveBatch(this, tasks); | ||||
| @@ -1328,11 +1320,11 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      function() { | ||||
|                          let userName = activatedItem.user.get_user_name(); | ||||
|                          this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME, | ||||
|                                                                               userName); | ||||
|                          this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME, | ||||
|                                                                                    userName, null); | ||||
|  | ||||
|                          if (this._haveFingerprintReader) | ||||
|                              this._greeterClient.call_begin_verification_for_user(_FINGERPRINT_SERVICE_NAME, userName); | ||||
|                              this._greeterClient.call_begin_verification_for_user_sync(_FINGERPRINT_SERVICE_NAME, userName, null); | ||||
|                      }]; | ||||
|  | ||||
|         this._user = activatedItem.user; | ||||
|   | ||||
| @@ -8,6 +8,8 @@ const St = imports.gi.St; | ||||
| const Params = imports.misc.params; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const DEFAULT_FADE_FACTOR = 0.4; | ||||
|  | ||||
| /** | ||||
|  * Lightbox: | ||||
|  * @container: parent Clutter.Container | ||||
| @@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener; | ||||
|  *           - inhibitEvents: whether to inhibit events for @container | ||||
|  *           - width: shade actor width | ||||
|  *           - height: shade actor height | ||||
|  *           - fadeTime: seconds used to fade in/out | ||||
|  *           - fadeInTime: seconds used to fade in | ||||
|  *           - fadeOutTime: seconds used to fade out | ||||
|  * | ||||
|  * Lightbox creates a dark translucent "shade" actor to hide the | ||||
|  * contents of @container, and allows you to specify particular actors | ||||
| @@ -38,12 +41,16 @@ const Lightbox = new Lang.Class({ | ||||
|         params = Params.parse(params, { inhibitEvents: false, | ||||
|                                         width: null, | ||||
|                                         height: null, | ||||
|                                         fadeTime: null | ||||
|                                         fadeInTime: null, | ||||
|                                         fadeOutTime: null, | ||||
|                                         fadeFactor: DEFAULT_FADE_FACTOR | ||||
|                                       }); | ||||
|  | ||||
|         this._container = container; | ||||
|         this._children = container.get_children(); | ||||
|         this._fadeTime = params.fadeTime; | ||||
|         this._fadeInTime = params.fadeInTime; | ||||
|         this._fadeOutTime = params.fadeOutTime; | ||||
|         this._fadeFactor = params.fadeFactor; | ||||
|         this.actor = new St.Bin({ x: 0, | ||||
|                                   y: 0, | ||||
|                                   style_class: 'lightbox', | ||||
| @@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({ | ||||
|         container.add_actor(this.actor); | ||||
|         this.actor.raise_top(); | ||||
|         this.actor.hide(); | ||||
|         this.shown = false; | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|  | ||||
| @@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         if (this._fadeTime) { | ||||
|         if (this._fadeInTime) { | ||||
|             this.shown = false; | ||||
|             this.actor.opacity = 0; | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { opacity: 255, | ||||
|                                time: this._fadeTime, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              { opacity: 255 * this._fadeFactor, | ||||
|                                time: this._fadeInTime, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this.shown = true; | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             this.actor.opacity = 255; | ||||
|             this.actor.opacity = 255 * this._fadeFactor; | ||||
|             this.shown = true; | ||||
|         } | ||||
|         this.actor.show(); | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         if (this._fadeTime) { | ||||
|         this.shown = false; | ||||
|         if (this._fadeOutTime) { | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: this._fadeTime, | ||||
|                                time: this._fadeOutTime, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this.actor.hide(); | ||||
|   | ||||
| @@ -29,6 +29,7 @@ const LookingGlass = imports.ui.lookingGlass; | ||||
| const NetworkAgent = imports.ui.networkAgent; | ||||
| const NotificationDaemon = imports.ui.notificationDaemon; | ||||
| const WindowAttentionHandler = imports.ui.windowAttentionHandler; | ||||
| const ScreenShield = imports.ui.screenShield; | ||||
| const Scripting = imports.ui.scripting; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const TelepathyClient = imports.ui.telepathyClient; | ||||
| @@ -51,6 +52,7 @@ let runDialog = null; | ||||
| let lookingGlass = null; | ||||
| let wm = null; | ||||
| let messageTray = null; | ||||
| let screenShield = null; | ||||
| let notificationDaemon = null; | ||||
| let windowAttentionHandler = null; | ||||
| let telepathyClient = null; | ||||
| @@ -216,6 +218,7 @@ function start() { | ||||
|     panel = new Panel.Panel(); | ||||
|     wm = new WindowManager.WindowManager(); | ||||
|     messageTray = new MessageTray.MessageTray(); | ||||
|     screenShield = new ScreenShield.ScreenShield(); | ||||
|     keyboard = new Keyboard.Keyboard(); | ||||
|     notificationDaemon = new NotificationDaemon.NotificationDaemon(); | ||||
|     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); | ||||
| @@ -491,8 +494,8 @@ function loadTheme() { | ||||
|  | ||||
|     let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); | ||||
|  | ||||
|     if (global.session_type == Shell.SessionType.GDM) | ||||
|         theme.load_stylesheet(_gdmCssStylesheet); | ||||
|     // FIXME: merge back into main stylesheet | ||||
|     theme.load_stylesheet(_gdmCssStylesheet); | ||||
|  | ||||
|     if (previousTheme) { | ||||
|         let customStylesheets = previousTheme.get_custom_stylesheets(); | ||||
| @@ -621,6 +624,10 @@ function _findModal(actor) { | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| function isInModalStack(actor) { | ||||
|     return _findModal(actor) != -1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * pushModal: | ||||
|  * @actor: #ClutterActor which will be given keyboard focus | ||||
| @@ -661,7 +668,7 @@ function pushModal(actor, timestamp, options) { | ||||
|     let actorDestroyId = actor.connect('destroy', function() { | ||||
|         let index = _findModal(actor); | ||||
|         if (index >= 0) | ||||
|             modalActorFocusStack.splice(index, 1); | ||||
|             popModal(actor); | ||||
|     }); | ||||
|     let curFocus = global.stage.get_key_focus(); | ||||
|     let curFocusDestroyId; | ||||
|   | ||||
							
								
								
									
										142
									
								
								js/ui/screenShield.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								js/ui/screenShield.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const UnlockDialog = imports.ui.unlockDialog; | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
|  | ||||
| /** | ||||
|  * To test screen shield, make sure to kill gnome-screensaver. | ||||
|  * | ||||
|  * If you are setting org.gnome.desktop.session.idle-delay directly in dconf, | ||||
|  * rather than through System Settings, you also need to set | ||||
|  * org.gnome.settings-daemon.plugins.power.sleep-display-ac and | ||||
|  * org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value. | ||||
|  * This will ensure that the screen blanks at the right time when it fades out. | ||||
|  * https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance. | ||||
|  */ | ||||
| const ScreenShield = new Lang.Class({ | ||||
|     Name: 'ScreenShield', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) { | ||||
|             this._onStatusChanged(proxy.status); | ||||
|         })); | ||||
|         this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) { | ||||
|             this._onStatusChanged(status); | ||||
|         })); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._isLocked = false; | ||||
|         this._group = new St.Widget({ x: 0, | ||||
|                                       y: 0 }); | ||||
|         Main.uiGroup.add_actor(this._group); | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE }); | ||||
|         this._group.add_constraint(constraint); | ||||
|  | ||||
|         this._lightbox = new Lightbox.Lightbox(this._group, | ||||
|                                                { inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 }); | ||||
|         this._background = Meta.BackgroundActor.new_for_screen(global.screen); | ||||
|         this._background.hide(); | ||||
|         Main.uiGroup.add_actor(this._background); | ||||
|     }, | ||||
|  | ||||
|     _onStatusChanged: function(status) { | ||||
|         log ("in _onStatusChanged"); | ||||
|         if (status == GnomeSession.PresenceStatus.IDLE) { | ||||
|             log("session gone idle"); | ||||
|  | ||||
|             if (this._dialog) { | ||||
|                 log('canceling existing dialog'); | ||||
|                 this._dialog.cancel(); | ||||
|                 this._dialog = null; | ||||
|             } | ||||
|  | ||||
|             this._group.reactive = true; | ||||
|             if (!this._isModal) { | ||||
|                 Main.pushModal(this._group); | ||||
|                 this._isModal = true; | ||||
|             } | ||||
|  | ||||
|             this._group.raise_top(); | ||||
|             this._lightbox.show(); | ||||
|         } else { | ||||
|             log('status is now ' + status); | ||||
|  | ||||
|             let lightboxWasShown = this._lightbox.shown; | ||||
|             log("this._lightbox.shown " + this._lightbox.shown); | ||||
|             this._lightbox.hide(); | ||||
|  | ||||
|             let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY); | ||||
|             if (shouldLock || this._isLocked) { | ||||
|                 this._isLocked = true; | ||||
|                 this._background.show(); | ||||
|                 this._background.raise_top(); | ||||
|  | ||||
|                 this._showUnlockDialog(); | ||||
|             } else if (this._isModal) { | ||||
|                 this._popModal(); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _popModal: function() { | ||||
|         this._group.reactive = false; | ||||
|         Main.popModal(this._group); | ||||
|  | ||||
|         this._background.hide(); | ||||
|     }, | ||||
|  | ||||
|     _showUnlockDialog: function() { | ||||
|         if (this._dialog) { | ||||
|             log ('_showUnlockDialog called again when the dialog was already there'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             this._dialog = new UnlockDialog.UnlockDialog(); | ||||
|             this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed)); | ||||
|             this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded)); | ||||
|  | ||||
|             if (!this._dialog.open(global.get_current_time())) | ||||
|                 throw new Error('open failed!') | ||||
|  | ||||
|             this._dialog._group.raise_top(); | ||||
|         } catch(e) { | ||||
|             // FIXME: this is for debugging purposes | ||||
|             logError(e, 'error while creating unlock dialog'); | ||||
|  | ||||
|             this._popModal(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onUnlockFailed: function() { | ||||
|         // FIXME: for now, on failure we just destroy the dialog and create a new | ||||
|         // one (this is what gnome-screensaver does) | ||||
|         // in the future, we may want to go back to the lock screen instead | ||||
|  | ||||
|         this._dialog.destroy(); | ||||
|         this._dialog = null; | ||||
|  | ||||
|         this._showUnlockDialog(); | ||||
|     }, | ||||
|  | ||||
|     _onUnlockSucceded: function() { | ||||
|         this._dialog.destroy(); | ||||
|         this._dialog = null; | ||||
|  | ||||
|         this._popModal(); | ||||
|     }, | ||||
| }); | ||||
							
								
								
									
										363
									
								
								js/ui/unlockDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								js/ui/unlockDialog.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,363 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const AccountsService = imports.gi.AccountsService; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GdmGreeter = imports.gi.GdmGreeter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Signals = imports.signals; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
|  | ||||
| const Fprint = imports.gdm.fingerprint; | ||||
| const GdmLoginDialog = imports.gdm.loginDialog; | ||||
|  | ||||
| function _fadeInActor(actor) { | ||||
|     if (actor.opacity == 255 && actor.visible) | ||||
|         return; | ||||
|  | ||||
|     actor.show(); | ||||
|     let [minHeight, naturalHeight] = actor.get_preferred_height(-1); | ||||
|  | ||||
|     actor.opacity = 0; | ||||
|     actor.set_height(0); | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 255, | ||||
|                        height: naturalHeight, | ||||
|                        time: _FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            this.set_height(-1); | ||||
|                        }, | ||||
|                      }); | ||||
| } | ||||
|  | ||||
| function _fadeOutActor(actor) { | ||||
|     if (!actor.visible || actor.opacity == 0) { | ||||
|         actor.opacity = 0; | ||||
|         actor.hide(); | ||||
|     } | ||||
|  | ||||
|     Tweener.addTween(actor, | ||||
|                      { opacity: 0, | ||||
|                        height: 0, | ||||
|                        time: _FADE_ANIMATION_TIME, | ||||
|                        transition: 'easeOutQuad', | ||||
|                        onComplete: function() { | ||||
|                            this.hide(); | ||||
|                            this.set_height(-1); | ||||
|                        }, | ||||
|                      }); | ||||
| } | ||||
|  | ||||
| // A widget showing the user avatar and name | ||||
| const UserWidget = new Lang.Class({ | ||||
|     Name: 'UserWidget', | ||||
|  | ||||
|     _init: function(user) { | ||||
|         this._user = user; | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ style_class: 'status-chooser', | ||||
|                                         vertical: false, | ||||
|                                         reactive: false | ||||
|                                       }); | ||||
|  | ||||
|         this._iconBin = new St.Bin({ style_class: 'status-chooser-user-icon' }); | ||||
|         this.actor.add(this._iconBin, | ||||
|                        { x_fill: true, | ||||
|                          y_fill: true }); | ||||
|  | ||||
|         this._label = new St.Label({ style_class: 'login-dialog-prompt-label', | ||||
|                                      // FIXME: | ||||
|                                      style: 'text-align: right' }); | ||||
|         this.actor.add(this._label, | ||||
|                       { expand: true, | ||||
|                         x_fill: true, | ||||
|                         y_fill: true | ||||
|                       }); | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', | ||||
|                                                 Lang.bind(this, | ||||
|                                                           this._updateUser)); | ||||
|         this._userChangedId = this._user.connect('changed', | ||||
|                                                  Lang.bind(this, | ||||
|                                                            this._updateUser)); | ||||
|         this.actor.connect('notify::mapped', Lang.bind(this, function() { | ||||
|             if (this.actor.mapped) | ||||
|                 this._updateUser(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         // clean up signal handlers | ||||
|         if (this._userLoadedId != 0) { | ||||
|             this._user.disconnect(this._userLoadedId); | ||||
|             this._userLoadedId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._userChangedId != 0) { | ||||
|             this._user.disconnect(this._userChangedId); | ||||
|             this._userChangedId = 0; | ||||
|         } | ||||
|  | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _updateUser: function() { | ||||
|         let iconFile = null; | ||||
|         if (this._user.is_loaded) { | ||||
|             this._label.text = this._user.get_real_name(); | ||||
|             iconFile = this._user.get_icon_file(); | ||||
|             if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS)) | ||||
|                 iconFile = null; | ||||
|         } else { | ||||
|             this._label.text = ""; | ||||
|         } | ||||
|  | ||||
|         if (iconFile) | ||||
|             this._setIconFromFile(iconFile); | ||||
|         else | ||||
|             this._setIconFromName('avatar-default'); | ||||
|     }, | ||||
|  | ||||
|     // XXX: a GFileIcon instead? | ||||
|     _setIconFromFile: function(iconFile) { | ||||
|         this._iconBin.set_style('background-image: url("' + iconFile + '");' + | ||||
|                                 'background-size: contain;'); | ||||
|         this._iconBin.child = null; | ||||
|     }, | ||||
|  | ||||
|     _setIconFromName: function(iconName) { | ||||
|         this._iconBin.set_style(null); | ||||
|  | ||||
|         if (iconName != null) { | ||||
|             let icon = new St.Icon({ icon_name: iconName, | ||||
|                                      icon_type: St.IconType.SYMBOLIC, | ||||
|                                      icon_size: DIALOG_ICON_SIZE }); | ||||
|             this._iconBin.child = icon; | ||||
|             this._iconBin.show(); | ||||
|         } else { | ||||
|             this._iconBin.child = null; | ||||
|             this._iconBin.hide(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const UnlockDialog = new Lang.Class({ | ||||
|     Name: 'UnlockDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent({ shellReactive: true, | ||||
|                       styleClass: 'login-dialog' }); | ||||
|  | ||||
|         this._userManager = AccountsService.UserManager.get_default(); | ||||
|         this._userName = GLib.get_user_name(); | ||||
|         this._user = this._userManager.get_user(this._userName); | ||||
|  | ||||
|         this._greeterClient = GdmGreeter.Server.new_for_display_sync(null, null); | ||||
|  | ||||
|         this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null); | ||||
|  | ||||
|         this._greeterClient.connect('reset', | ||||
|                                     Lang.bind(this, this._onReset)); | ||||
|         this._greeterClient.connect('ready', | ||||
|                                     Lang.bind(this, this._onReady)); | ||||
|         this._greeterClient.connect('info', | ||||
|                                     Lang.bind(this, this._onInfo)); | ||||
|         this._greeterClient.connect('problem', | ||||
|                                     Lang.bind(this, this._onProblem)); | ||||
|         this._greeterClient.connect('info-query', | ||||
|                                     Lang.bind(this, this._onInfoQuery)); | ||||
|         this._greeterClient.connect('secret-info-query', | ||||
|                                     Lang.bind(this, this._onSecretInfoQuery)); | ||||
|         this._greeterClient.connect('session-opened', | ||||
|                                     Lang.bind(this, this._onSessionOpened)); | ||||
|         this._greeterClient.connect('conversation-stopped', | ||||
|                                     Lang.bind(this, this._onConversationStopped)); | ||||
|  | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._startFingerprintConversationIfNeeded(); | ||||
|  | ||||
|         this._userWidget = new UserWidget(this._user); | ||||
|         this.contentLayout.add_actor(this._userWidget.actor); | ||||
|  | ||||
|         this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout', | ||||
|                                                 vertical: false | ||||
|                                               }); | ||||
|  | ||||
|         this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' }); | ||||
|         this._promptLayout.add(this._promptLabel, | ||||
|                             { expand: false, | ||||
|                               x_fill: true, | ||||
|                               y_fill: true, | ||||
|                               x_align: St.Align.START }); | ||||
|  | ||||
|         this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
|                                            can_focus: true }); | ||||
|         this._promptLayout.add(this._promptEntry, | ||||
|                             { expand: true, | ||||
|                               x_fill: true, | ||||
|                               y_fill: false, | ||||
|                               x_align: St.Align.START }); | ||||
|  | ||||
|         this.contentLayout.add_actor(this._promptLayout); | ||||
|  | ||||
|         // Translators: this message is shown below the password entry field | ||||
|         // to indicate the user can swipe their finger instead | ||||
|         this._promptFingerprintMessage = new St.Label({ text: _("(or swipe finger)"), | ||||
|                                                         style_class: 'login-dialog-prompt-fingerprint-message' }); | ||||
|         this._promptFingerprintMessage.hide(); | ||||
|         this.contentLayout.add_actor(this._promptFingerprintMessage); | ||||
|          | ||||
|         this._okButton = { label: _("Unlock"), | ||||
|                            action: Lang.bind(this, this._doUnlock), | ||||
|                            key: Clutter.KEY_Return, | ||||
|                          }; | ||||
|         this.setButtons([this._okButton]); | ||||
|  | ||||
|         this._updateOkButton(true); | ||||
|     }, | ||||
|  | ||||
|     _updateOkButton: function(sensitive) { | ||||
|         this._okButton.button.reactive = sensitive; | ||||
|         this._okButton.button.can_focus = sensitive; | ||||
|         if (sensitive) | ||||
|             this._okButton.button.remove_style_pseudo_class('disabled'); | ||||
|         else | ||||
|             this._okButton.button.add_style_pseudo_class('disabled'); | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         // I'm not sure this is emitted for external greeters... | ||||
|  | ||||
|         this._greeterClient.call_start_conversation_sync(GdmLoginDialog.PASSWORD_SERVICE_NAME, null); | ||||
|         this._startFingerprintConversationIfNeeded(); | ||||
|     }, | ||||
|  | ||||
|    _startFingerprintConversationIfNeeded: function() { | ||||
|        this._haveFingerprintReader = false; | ||||
|  | ||||
|        // FIXME: the greeter has a GSettings key for disabling fingerprint auth | ||||
|  | ||||
|        this._fprintManager.GetDefaultDeviceRemote(Lang.bind(this, | ||||
|            function(device, error) { | ||||
|                if (!error && device) | ||||
|                    this._haveFingerprintReader = true; | ||||
|  | ||||
|                if (this._haveFingerprintReader) | ||||
|                    this._greeterClient.call_start_conversation_sync(GdmLoginDialog.FINGERPRINT_SERVICE_NAME, null); | ||||
|            })); | ||||
|     }, | ||||
|  | ||||
|     _onReady: function(greeter, serviceName) { | ||||
|         greeter.call_begin_verification_for_user_sync(serviceName, this._userName, null); | ||||
|     }, | ||||
|  | ||||
|     _onInfo: function(greeter, serviceName, info) { | ||||
|         // We don't display fingerprint messages, because they | ||||
|         // have words like UPEK in them. Instead we use the messages | ||||
|         // as a cue to display our own message. | ||||
|         if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME && | ||||
|             this._haveFingerprintReader && | ||||
|             (!this._promptFingerprintMessage.visible || | ||||
|              this._promptFingerprintMessage.opacity != 255)) { | ||||
|  | ||||
|             _fadeInActor(this._promptFingerprintMessage); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notify(info); | ||||
|     }, | ||||
|  | ||||
|     _onProblem: function(client, serviceName, problem) { | ||||
|         // we don't want to show auth failed messages to | ||||
|         // users who haven't enrolled their fingerprint. | ||||
|         if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|         Main.notifyError(problem); | ||||
|     }, | ||||
|  | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
|         // We only expect questions to come from the main auth service | ||||
|         if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptLabel.text = question; | ||||
|         this._promptEntry.text = ''; | ||||
|         this._promptEntry.clutter_text.set_password_char(''); | ||||
|  | ||||
|         this._currentQuery = serviceName; | ||||
|         this._updateOkButton(true); | ||||
|     }, | ||||
|  | ||||
|     _onSecretInfoQuery: function(client, serviceName, secretQuestion) { | ||||
|         // We only expect secret requests to come from the main auth service | ||||
|         if (serviceName != GdmLoginDialog.PASSWORD_SERVICE_NAME) | ||||
|             return; | ||||
|  | ||||
|         this._promptLabel.text = secretQuestion; | ||||
|         this._promptEntry.text = ''; | ||||
|         this._promptEntry.clutter_text.set_password_char('\u25cf'); | ||||
|  | ||||
|         this._currentQuery = serviceName; | ||||
|         this._updateOkButton(true); | ||||
|     }, | ||||
|  | ||||
|     _doUnlock: function() { | ||||
|         if (!this._currentQuery) | ||||
|             return; | ||||
|  | ||||
|         let query = this._currentQuery; | ||||
|         this._currentQuery = null; | ||||
|  | ||||
|         this._updateOkButton(false); | ||||
|  | ||||
|         this._greeterClient.call_answer_query_sync(query, this._promptEntry.text, null); | ||||
|     }, | ||||
|  | ||||
|     _onConversationStopped: function(client, serviceName) { | ||||
|         // if the password service fails, then cancel everything. | ||||
|         // But if, e.g., fingerprint fails, still give | ||||
|         // password authentication a chance to succeed | ||||
|         if (serviceName == GdmLoginDialog.PASSWORD_SERVICE_NAME) { | ||||
|             this._greeterClient.call_cancel_sync(null); | ||||
|             this.emit('failed'); | ||||
|         } else if (serviceName == GdmLoginDialog.FINGERPRINT_SERVICE_NAME) { | ||||
|             _fadeOutActor(this._promptFingerprintMessage); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onSessionOpened: function(client, serviceName) { | ||||
|         // For external greeters, SessionOpened means we succeded | ||||
|         // in the authentication process | ||||
|  | ||||
|         // Close the greeter proxy | ||||
|         this._greeterClient.run_dispose(); | ||||
|         this._greeterClient = null; | ||||
|  | ||||
|         this.emit('unlocked'); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         if (this._greeterClient) { | ||||
|             this._greeterClient.run_dispose(); | ||||
|             this._greeterClient = null; | ||||
|         } | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         this._greeterClient.call_cancel_sync(null); | ||||
|  | ||||
|         this.destroy(); | ||||
|     }, | ||||
| }); | ||||
| @@ -301,7 +301,8 @@ const WindowClone = new Lang.Class({ | ||||
|  | ||||
|         if (!this._zoomLightbox) | ||||
|             this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup, | ||||
|                                                        { fadeTime: LIGHTBOX_FADE_TIME }); | ||||
|                                                        { fadeInTime: LIGHTBOX_FADE_TIME, | ||||
|                                                          fadeOutTime: LIGHTBOX_FADE_TIME }); | ||||
|         this._zoomLightbox.show(); | ||||
|  | ||||
|         this._zoomLocalOrig  = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user