Compare commits
6 Commits
citadel
...
wip/screen
Author | SHA1 | Date | |
---|---|---|---|
|
352ad8f558 | ||
|
d5dc886748 | ||
|
17044adf96 | ||
|
9dfd805be2 | ||
|
bbbb6b685f | ||
|
71c0d5f82d |
@ -1715,7 +1715,7 @@ StScrollBar StButton#vhandle:hover
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lightbox {
|
.lightbox {
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flashspot {
|
.flashspot {
|
||||||
|
@ -78,6 +78,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/popupMenu.js \
|
ui/popupMenu.js \
|
||||||
ui/remoteSearch.js \
|
ui/remoteSearch.js \
|
||||||
ui/runDialog.js \
|
ui/runDialog.js \
|
||||||
|
ui/screenShield.js \
|
||||||
ui/scripting.js \
|
ui/scripting.js \
|
||||||
ui/search.js \
|
ui/search.js \
|
||||||
ui/searchDisplay.js \
|
ui/searchDisplay.js \
|
||||||
@ -91,6 +92,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/status/bluetooth.js \
|
ui/status/bluetooth.js \
|
||||||
ui/telepathyClient.js \
|
ui/telepathyClient.js \
|
||||||
ui/tweener.js \
|
ui/tweener.js \
|
||||||
|
ui/unlockDialog.js \
|
||||||
ui/userMenu.js \
|
ui/userMenu.js \
|
||||||
ui/viewSelector.js \
|
ui/viewSelector.js \
|
||||||
ui/wanda.js \
|
ui/wanda.js \
|
||||||
|
@ -39,8 +39,8 @@ const Main = imports.ui.main;
|
|||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const _PASSWORD_SERVICE_NAME = 'gdm-password';
|
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||||
const _FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||||
const _FADE_ANIMATION_TIME = 0.16;
|
const _FADE_ANIMATION_TIME = 0.16;
|
||||||
const _RESIZE_ANIMATION_TIME = 0.25;
|
const _RESIZE_ANIMATION_TIME = 0.25;
|
||||||
const _SCROLL_ANIMATION_TIME = 2.0;
|
const _SCROLL_ANIMATION_TIME = 2.0;
|
||||||
@ -747,15 +747,13 @@ const LoginDialog = new Lang.Class({
|
|||||||
Lang.bind(this, this._onOpened));
|
Lang.bind(this, this._onOpened));
|
||||||
|
|
||||||
this._userManager = AccountsService.UserManager.get_default()
|
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_sync(PASSWORD_SERVICE_NAME, null);
|
||||||
|
|
||||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
|
||||||
|
|
||||||
this._greeterClient.connect('reset',
|
this._greeterClient.connect('reset',
|
||||||
Lang.bind(this, this._onReset));
|
Lang.bind(this, this._onReset));
|
||||||
this._greeterClient.connect('default-session-changed',
|
this._greeterClient.connect('default-session-name-changed',
|
||||||
Lang.bind(this, this._onDefaultSessionChanged));
|
Lang.bind(this, this._onDefaultSessionChanged));
|
||||||
this._greeterClient.connect('info',
|
this._greeterClient.connect('info',
|
||||||
Lang.bind(this, this._onInfo));
|
Lang.bind(this, this._onInfo));
|
||||||
@ -769,8 +767,6 @@ const LoginDialog = new Lang.Class({
|
|||||||
Lang.bind(this, this._onSessionOpened));
|
Lang.bind(this, this._onSessionOpened));
|
||||||
this._greeterClient.connect('timed-login-requested',
|
this._greeterClient.connect('timed-login-requested',
|
||||||
Lang.bind(this, this._onTimedLoginRequested));
|
Lang.bind(this, this._onTimedLoginRequested));
|
||||||
this._greeterClient.connect('authentication-failed',
|
|
||||||
Lang.bind(this, this._onAuthenticationFailed));
|
|
||||||
this._greeterClient.connect('conversation-stopped',
|
this._greeterClient.connect('conversation-stopped',
|
||||||
Lang.bind(this, this._onConversationStopped));
|
Lang.bind(this, this._onConversationStopped));
|
||||||
|
|
||||||
@ -899,7 +895,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._haveFingerprintReader = true;
|
this._haveFingerprintReader = true;
|
||||||
|
|
||||||
if (this._haveFingerprintReader)
|
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) {
|
_onReset: function(client, serviceName) {
|
||||||
this._greeterClient.call_start_conversation(_PASSWORD_SERVICE_NAME);
|
this._greeterClient.call_start_conversation_sync(PASSWORD_SERVICE_NAME, null);
|
||||||
this._startFingerprintConversationIfNeeded();
|
this._startFingerprintConversationIfNeeded();
|
||||||
|
|
||||||
let tasks = [this._hidePrompt,
|
let tasks = [this._hidePrompt,
|
||||||
@ -954,7 +950,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
// We don't display fingerprint messages, because they
|
// We don't display fingerprint messages, because they
|
||||||
// have words like UPEK in them. Instead we use the messages
|
// have words like UPEK in them. Instead we use the messages
|
||||||
// as a cue to display our own message.
|
// as a cue to display our own message.
|
||||||
if (serviceName == _FINGERPRINT_SERVICE_NAME &&
|
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||||
this._haveFingerprintReader &&
|
this._haveFingerprintReader &&
|
||||||
(!this._promptFingerprintMessage.visible ||
|
(!this._promptFingerprintMessage.visible ||
|
||||||
this._promptFingerprintMessage.opacity != 255)) {
|
this._promptFingerprintMessage.opacity != 255)) {
|
||||||
@ -963,7 +959,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
return;
|
return;
|
||||||
Main.notifyError(info);
|
Main.notifyError(info);
|
||||||
},
|
},
|
||||||
@ -971,13 +967,13 @@ const LoginDialog = new Lang.Class({
|
|||||||
_onProblem: function(client, serviceName, problem) {
|
_onProblem: function(client, serviceName, problem) {
|
||||||
// we don't want to show auth failed messages to
|
// we don't want to show auth failed messages to
|
||||||
// users who haven't enrolled their fingerprint.
|
// users who haven't enrolled their fingerprint.
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
return;
|
return;
|
||||||
Main.notifyError(problem);
|
Main.notifyError(problem);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancel: function(client) {
|
_onCancel: function(client) {
|
||||||
this._greeterClient.call_cancel();
|
this._greeterClient.call_cancel_sync(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInPrompt: function() {
|
_fadeInPrompt: function() {
|
||||||
@ -1084,7 +1080,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
let _text = this._promptEntry.get_text();
|
let _text = this._promptEntry.get_text();
|
||||||
this._promptEntry.reactive = false;
|
this._promptEntry.reactive = false;
|
||||||
this._promptEntry.add_style_pseudo_class('insensitive');
|
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);
|
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||||
@ -1092,7 +1088,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
_onInfoQuery: function(client, serviceName, question) {
|
_onInfoQuery: function(client, serviceName, question) {
|
||||||
// We only expect questions to come from the main auth service
|
// We only expect questions to come from the main auth service
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._promptEntry.set_text('');
|
this._promptEntry.set_text('');
|
||||||
@ -1102,7 +1098,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||||
// We only expect secret requests to come from the main auth service
|
// We only expect secret requests to come from the main auth service
|
||||||
if (serviceName != _PASSWORD_SERVICE_NAME)
|
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._promptEntry.set_text('');
|
this._promptEntry.set_text('');
|
||||||
@ -1111,7 +1107,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onSessionOpened: function(client, serviceName) {
|
_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) {
|
_waitForItemForUser: function(userName) {
|
||||||
@ -1193,7 +1189,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
function() {
|
function() {
|
||||||
this._timedLoginBatch = null;
|
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);
|
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) {
|
_onConversationStopped: function(client, serviceName) {
|
||||||
// if the password service fails, then cancel everything.
|
// if the password service fails, then cancel everything.
|
||||||
// But if, e.g., fingerprint fails, still give
|
// But if, e.g., fingerprint fails, still give
|
||||||
// password authentication a chance to succeed
|
// password authentication a chance to succeed
|
||||||
if (serviceName == _PASSWORD_SERVICE_NAME) {
|
if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||||
this._greeterClient.call_cancel();
|
this._greeterClient.call_cancel_sync(null);
|
||||||
} else if (serviceName == _FINGERPRINT_SERVICE_NAME) {
|
} else if (serviceName == FINGERPRINT_SERVICE_NAME) {
|
||||||
_fadeOutActor(this._promptFingerprintMessage);
|
_fadeOutActor(this._promptFingerprintMessage);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1269,7 +1261,7 @@ const LoginDialog = new Lang.Class({
|
|||||||
this._fadeOutLogo]),
|
this._fadeOutLogo]),
|
||||||
|
|
||||||
function() {
|
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);
|
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||||
@ -1328,11 +1320,11 @@ const LoginDialog = new Lang.Class({
|
|||||||
|
|
||||||
function() {
|
function() {
|
||||||
let userName = activatedItem.user.get_user_name();
|
let userName = activatedItem.user.get_user_name();
|
||||||
this._greeterClient.call_begin_verification_for_user(_PASSWORD_SERVICE_NAME,
|
this._greeterClient.call_begin_verification_for_user_sync(PASSWORD_SERVICE_NAME,
|
||||||
userName);
|
userName, null);
|
||||||
|
|
||||||
if (this._haveFingerprintReader)
|
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;
|
this._user = activatedItem.user;
|
||||||
|
@ -8,6 +8,8 @@ const St = imports.gi.St;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const DEFAULT_FADE_FACTOR = 0.4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lightbox:
|
* Lightbox:
|
||||||
* @container: parent Clutter.Container
|
* @container: parent Clutter.Container
|
||||||
@ -15,7 +17,8 @@ const Tweener = imports.ui.tweener;
|
|||||||
* - inhibitEvents: whether to inhibit events for @container
|
* - inhibitEvents: whether to inhibit events for @container
|
||||||
* - width: shade actor width
|
* - width: shade actor width
|
||||||
* - height: shade actor height
|
* - 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
|
* Lightbox creates a dark translucent "shade" actor to hide the
|
||||||
* contents of @container, and allows you to specify particular actors
|
* 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,
|
params = Params.parse(params, { inhibitEvents: false,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
fadeTime: null
|
fadeInTime: null,
|
||||||
|
fadeOutTime: null,
|
||||||
|
fadeFactor: DEFAULT_FADE_FACTOR
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._children = container.get_children();
|
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,
|
this.actor = new St.Bin({ x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
style_class: 'lightbox',
|
style_class: 'lightbox',
|
||||||
@ -52,6 +59,7 @@ const Lightbox = new Lang.Class({
|
|||||||
container.add_actor(this.actor);
|
container.add_actor(this.actor);
|
||||||
this.actor.raise_top();
|
this.actor.raise_top();
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
this.shown = false;
|
||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
@ -93,24 +101,30 @@ const Lightbox = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if (this._fadeTime) {
|
if (this._fadeInTime) {
|
||||||
|
this.shown = false;
|
||||||
this.actor.opacity = 0;
|
this.actor.opacity = 0;
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255 * this._fadeFactor,
|
||||||
time: this._fadeTime,
|
time: this._fadeInTime,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.shown = true;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.actor.opacity = 255;
|
this.actor.opacity = 255 * this._fadeFactor;
|
||||||
|
this.shown = true;
|
||||||
}
|
}
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
if (this._fadeTime) {
|
this.shown = false;
|
||||||
|
if (this._fadeOutTime) {
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: this._fadeTime,
|
time: this._fadeOutTime,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
|
@ -29,6 +29,7 @@ const LookingGlass = imports.ui.lookingGlass;
|
|||||||
const NetworkAgent = imports.ui.networkAgent;
|
const NetworkAgent = imports.ui.networkAgent;
|
||||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||||
|
const ScreenShield = imports.ui.screenShield;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
const TelepathyClient = imports.ui.telepathyClient;
|
const TelepathyClient = imports.ui.telepathyClient;
|
||||||
@ -51,6 +52,7 @@ let runDialog = null;
|
|||||||
let lookingGlass = null;
|
let lookingGlass = null;
|
||||||
let wm = null;
|
let wm = null;
|
||||||
let messageTray = null;
|
let messageTray = null;
|
||||||
|
let screenShield = null;
|
||||||
let notificationDaemon = null;
|
let notificationDaemon = null;
|
||||||
let windowAttentionHandler = null;
|
let windowAttentionHandler = null;
|
||||||
let telepathyClient = null;
|
let telepathyClient = null;
|
||||||
@ -216,6 +218,7 @@ function start() {
|
|||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
|
screenShield = new ScreenShield.ScreenShield();
|
||||||
keyboard = new Keyboard.Keyboard();
|
keyboard = new Keyboard.Keyboard();
|
||||||
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||||
@ -491,8 +494,8 @@ function loadTheme() {
|
|||||||
|
|
||||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||||
|
|
||||||
if (global.session_type == Shell.SessionType.GDM)
|
// FIXME: merge back into main stylesheet
|
||||||
theme.load_stylesheet(_gdmCssStylesheet);
|
theme.load_stylesheet(_gdmCssStylesheet);
|
||||||
|
|
||||||
if (previousTheme) {
|
if (previousTheme) {
|
||||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
@ -621,6 +624,10 @@ function _findModal(actor) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInModalStack(actor) {
|
||||||
|
return _findModal(actor) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pushModal:
|
* pushModal:
|
||||||
* @actor: #ClutterActor which will be given keyboard focus
|
* @actor: #ClutterActor which will be given keyboard focus
|
||||||
@ -661,7 +668,7 @@ function pushModal(actor, timestamp, options) {
|
|||||||
let actorDestroyId = actor.connect('destroy', function() {
|
let actorDestroyId = actor.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
modalActorFocusStack.splice(index, 1);
|
popModal(actor);
|
||||||
});
|
});
|
||||||
let curFocus = global.stage.get_key_focus();
|
let curFocus = global.stage.get_key_focus();
|
||||||
let curFocusDestroyId;
|
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)
|
if (!this._zoomLightbox)
|
||||||
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||||
{ fadeTime: LIGHTBOX_FADE_TIME });
|
{ fadeInTime: LIGHTBOX_FADE_TIME,
|
||||||
|
fadeOutTime: LIGHTBOX_FADE_TIME });
|
||||||
this._zoomLightbox.show();
|
this._zoomLightbox.show();
|
||||||
|
|
||||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||||
|
Loading…
Reference in New Issue
Block a user