Handle global keybindings in the shell
Handling global keybindings, such as volume and brightness keys but also custom keybindings, directly in the compositor is the only way to deal with grabs and modal operations that could be active (primarily the overview, for which special policy was introduced in the last commit) https://bugzilla.gnome.org/show_bug.cgi?id=613543
This commit is contained in:
parent
d886dc17e1
commit
60d87ef4ba
@ -2325,3 +2325,17 @@ StScrollBar StButton#vhandle:active {
|
|||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.osd-window {
|
||||||
|
color: #ededed;
|
||||||
|
background-color: rgba(33, 37, 38, 0.80);
|
||||||
|
border-radius: 15px;
|
||||||
|
text-shadow: 0 1px rgba(0, 0, 0, 0.75);
|
||||||
|
|
||||||
|
padding: 40px;
|
||||||
|
spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osd-progress-bar {
|
||||||
|
height: 0.8em;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
@ -100,6 +100,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/components/__init__.js \
|
ui/components/__init__.js \
|
||||||
ui/components/autorunManager.js \
|
ui/components/autorunManager.js \
|
||||||
ui/components/automountManager.js \
|
ui/components/automountManager.js \
|
||||||
|
ui/components/mediaKeysManager.js \
|
||||||
ui/components/networkAgent.js \
|
ui/components/networkAgent.js \
|
||||||
ui/components/polkitAgent.js \
|
ui/components/polkitAgent.js \
|
||||||
ui/components/recorder.js \
|
ui/components/recorder.js \
|
||||||
|
@ -17,6 +17,9 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
|
|||||||
<method name='Suspend'>
|
<method name='Suspend'>
|
||||||
<arg type='b' direction='in'/>
|
<arg type='b' direction='in'/>
|
||||||
</method>
|
</method>
|
||||||
|
<method name='Hibernate'>
|
||||||
|
<arg type='b' direction='in'/>
|
||||||
|
</method>
|
||||||
<method name='CanPowerOff'>
|
<method name='CanPowerOff'>
|
||||||
<arg type='s' direction='out'/>
|
<arg type='s' direction='out'/>
|
||||||
</method>
|
</method>
|
||||||
@ -26,6 +29,9 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
|
|||||||
<method name='CanSuspend'>
|
<method name='CanSuspend'>
|
||||||
<arg type='s' direction='out'/>
|
<arg type='s' direction='out'/>
|
||||||
</method>
|
</method>
|
||||||
|
<method name='CanHibernate'>
|
||||||
|
<arg type='s' direction='out'/>
|
||||||
|
</method>
|
||||||
</interface>;
|
</interface>;
|
||||||
|
|
||||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||||
@ -140,6 +146,15 @@ const LoginManagerSystemd = new Lang.Class({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canHibernate: function(asyncCallback) {
|
||||||
|
this._proxy.CanSuspendRemote(function(result, error) {
|
||||||
|
if (error)
|
||||||
|
asyncCallback(false);
|
||||||
|
else
|
||||||
|
asyncCallback(result[0] != 'no');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
powerOff: function() {
|
powerOff: function() {
|
||||||
this._proxy.PowerOffRemote(true);
|
this._proxy.PowerOffRemote(true);
|
||||||
},
|
},
|
||||||
@ -150,6 +165,10 @@ const LoginManagerSystemd = new Lang.Class({
|
|||||||
|
|
||||||
suspend: function() {
|
suspend: function() {
|
||||||
this._proxy.SuspendRemote(true);
|
this._proxy.SuspendRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
hibernate: function() {
|
||||||
|
this._proxy.HibernateRemote(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,6 +234,13 @@ const LoginManagerConsoleKit = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canHibernate: function(asyncCallback) {
|
||||||
|
Mainloop.idle_add(Lang.bind(this, function() {
|
||||||
|
asyncCallback(this._upClient.get_can_hibernate());
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
powerOff: function() {
|
powerOff: function() {
|
||||||
this._proxy.StopRemote();
|
this._proxy.StopRemote();
|
||||||
},
|
},
|
||||||
@ -225,5 +251,9 @@ const LoginManagerConsoleKit = new Lang.Class({
|
|||||||
|
|
||||||
suspend: function() {
|
suspend: function() {
|
||||||
this._upClient.suspend_sync(null);
|
this._upClient.suspend_sync(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
hibernate: function() {
|
||||||
|
this._upClient.hibernate_sync(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
647
js/ui/components/mediaKeysManager.js
Normal file
647
js/ui/components/mediaKeysManager.js
Normal file
@ -0,0 +1,647 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gdk = imports.gi.Gdk;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
|
const INTERFACE_SETTINGS = 'org.gnome.desktop.interface';
|
||||||
|
const POWER_SETTINGS = 'org.gnome.settings-daemon.plugins.power';
|
||||||
|
const XSETTINGS_SETTINGS = 'org.gnome.settings-daemon.plugins.xsettings';
|
||||||
|
const TOUCHPAD_SETTINGS = 'org.gnome.settings-daemon.peripherals.touchpad';
|
||||||
|
const KEYBINDING_SETTINGS = 'org.gnome.settings-daemon.plugins.media-keys';
|
||||||
|
const CUSTOM_KEYBINDING_SETTINGS = 'org.gnome.settings-daemon.plugins.media-keys.custom-keybinding';
|
||||||
|
const A11Y_SETTINGS = 'org.gnome.desktop.a11y.applications';
|
||||||
|
const MAGNIFIER_SETTINGS = 'org.gnome.desktop.a11y.magnifier';
|
||||||
|
const INPUT_SOURCE_SETTINGS = 'org.gnome.desktop.input-sources';
|
||||||
|
|
||||||
|
const MediaKeysInterface = <interface name='org.gnome.SettingsDaemon.MediaKeys'>
|
||||||
|
<method name='GrabMediaPlayerKeys'>
|
||||||
|
<arg name='application' direction='in' type='s'/>
|
||||||
|
<arg name='time' direction='in' type='u'/>
|
||||||
|
</method>
|
||||||
|
<method name='ReleaseMediaPlayerKeys'>
|
||||||
|
<arg name='application' direction='in' type='s'/>
|
||||||
|
</method>
|
||||||
|
<signal name='MediaPlayerKeyPressed'>
|
||||||
|
<arg name='application' type='s'/>
|
||||||
|
<arg name='key' type='s'/>
|
||||||
|
</signal>
|
||||||
|
</interface>;
|
||||||
|
|
||||||
|
/* [ actionName, setting, hardcodedKeysym, overviewOnly, args ] */
|
||||||
|
/* (overviewOnly means that the keybinding is handled when the shell is not
|
||||||
|
modal, or when the overview is active, but not when other modal operations
|
||||||
|
are active; otherwise the keybinding is always handled) */
|
||||||
|
const DEFAULT_KEYBINDINGS = [
|
||||||
|
[ 'doTouchpadToggle', null, 'XF86TouchpadToggle', false ],
|
||||||
|
[ 'doTouchpadSet', null, 'XF86TouchpadOn', false, [ true ] ],
|
||||||
|
[ 'doTouchpadSet', null, 'XF86TouchpadOff', false, [ false ] ],
|
||||||
|
[ 'doMute', 'volume-mute', null, false, [ false ] ],
|
||||||
|
[ 'doVolumeAdjust', 'volume-down', null, false, [ Clutter.ScrollDirection.DOWN, false ] ],
|
||||||
|
[ 'doVolumeAdjust', 'volume-up', null, false, [ Clutter.ScrollDirection.UP, false ] ],
|
||||||
|
[ 'doMute', null, '<Alt>XF86AudioMute', false, [ true ] ],
|
||||||
|
[ 'doVolumeAdjust', null, '<Alt>XF86AudioLowerVolume', false, [ Clutter.ScrollDirection.DOWN, true ] ],
|
||||||
|
[ 'doVolumeAdjust', null, '<Alt>XF86AudioRaiseVolume', false, [ Clutter.ScrollDirection.UP, true ] ],
|
||||||
|
[ 'doLogout', 'logout', null, true ],
|
||||||
|
[ 'doEject', 'eject', null, false ],
|
||||||
|
[ 'doHome', 'home', null, true ],
|
||||||
|
[ 'doLaunchMimeHandler', 'media', null, true, [ 'application/x-vorbis+ogg' ] ],
|
||||||
|
[ 'doLaunchApp', 'calculator', null, true, [ 'gcalcltool.desktop' ] ],
|
||||||
|
[ 'doLaunchApp', 'search', null, true, [ 'tracker-needle.desktop' ] ],
|
||||||
|
[ 'doLaunchMimeHandler', 'email', null, true, [ 'x-scheme-handler/mailto' ] ],
|
||||||
|
[ 'doScreensaver', 'screensaver', null, true ],
|
||||||
|
[ 'doScreensaver', null, 'XF86ScreenSaver', true ],
|
||||||
|
[ 'doLaunchApp', 'help', null, true, [ 'yelp.desktop' ] ],
|
||||||
|
[ 'doSpawn', 'screenshot', null, true, [ ['gnome-screenshot'] ] ],
|
||||||
|
[ 'doSpawn', 'window-screenshot', null, true, [ ['gnome-screenshot', '--window'] ] ],
|
||||||
|
[ 'doSpawn', 'area-screenshot', null, true, [ ['gnome-screenshot', '--area'] ] ],
|
||||||
|
[ 'doSpawn', 'screenshot-clip', null, true, [ ['gnome-screenshot', '--clipboard'] ] ],
|
||||||
|
[ 'doSpawn', 'window-screenshot-clip', null, true, [ ['gnome-screenshot', '--window', '--clipboard'] ] ],
|
||||||
|
[ 'doSpawn', 'area-screenshot-clip', null, true, [ ['gnome-screenshot', '--area', '--clipboard'] ] ],
|
||||||
|
[ 'doLaunchMimeHandler', 'www', null, true, [ 'x-scheme-handler/http' ] ],
|
||||||
|
[ 'doMediaKey', 'play', null, true, [ 'Play' ] ],
|
||||||
|
[ 'doMediaKey', 'pause', null, true, [ 'Pause' ] ],
|
||||||
|
[ 'doMediaKey', 'stop', null, true, [ 'Stop' ] ],
|
||||||
|
[ 'doMediaKey', 'previous', null, true, [ 'Previous' ] ],
|
||||||
|
[ 'doMediaKey', 'next', null, true, [ 'Next' ] ],
|
||||||
|
[ 'doMediaKey', null, 'XF86AudioRewind', true, [ 'Rewind' ] ],
|
||||||
|
[ 'doMediaKey', null, 'XF86AudioForward', true, [ 'FastForward' ] ],
|
||||||
|
[ 'doMediaKey', null, 'XF86AudioRepeat', true, [ 'Repeat' ] ],
|
||||||
|
[ 'doMediaKey', null, 'XF86AudioRandomPlay', true, [ 'Shuffle' ] ],
|
||||||
|
[ 'doXRandRAction', null, '<Super>p', false, [ 'VideoModeSwitch' ] ],
|
||||||
|
/* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */
|
||||||
|
[ 'doXRandRAction', null, 'XF86Display', false, [ 'VideoModeSwitch' ] ],
|
||||||
|
/* Key code of the XF86RotateWindows key (present on some tablets) */
|
||||||
|
[ 'doXRandRAction', null, 'XF86RotateWindows', false, [ 'Rotate' ] ],
|
||||||
|
[ 'doA11yAction', 'magnifier', null, true, [ 'screen-magnifier-enabled' ] ],
|
||||||
|
[ 'doA11yAction', 'screenreader', null, true, [ 'screen-reader-enabled' ] ],
|
||||||
|
[ 'doA11yAction', 'on-screen-keyboard', null, true, [ 'screen-keyboard-enabled' ] ],
|
||||||
|
[ 'doTextSize', 'increase-text-size', null, true, [ 1 ] ],
|
||||||
|
[ 'doTextSize', 'decrease-text-size', null, true, [ -1 ] ],
|
||||||
|
[ 'doToggleContrast', 'toggle-contrast', null, true ],
|
||||||
|
[ 'doMagnifierZoom', 'magnifier-zoom-in', null, true, [ 1 ] ],
|
||||||
|
[ 'doMagnifierZoom', 'magnifier-zoom-out', null, true, [ -1 ] ],
|
||||||
|
[ 'doPowerAction', null, 'XF86PowerOff', true, [ 'button-power' ] ],
|
||||||
|
/* the kernel / Xorg names really are like this... */
|
||||||
|
[ 'doPowerAction', null, 'XF86Suspend', false, [ 'button-sleep' ] ],
|
||||||
|
[ 'doPowerAction', null, 'XF86Sleep', false, [ 'button-suspend' ] ],
|
||||||
|
[ 'doPowerAction', null, 'XF86Hibernate', false, [ 'button-hibernate' ] ],
|
||||||
|
[ 'doBrightness', null, 'XF86MonBrightnessUp', false, [ 'Screen', 'StepUp' ] ],
|
||||||
|
[ 'doBrightness', null, 'XF86MonBrightnessDown', false, [ 'Screen', 'StepDown' ] ],
|
||||||
|
[ 'doBrightness', null, 'XF86KbdBrightnessUp', false, [ 'Keyboard', 'StepUp' ] ],
|
||||||
|
[ 'doBrightness', null, 'XF86KbdBrightnessDown', false, [ 'Keyboard', 'StepDown' ] ],
|
||||||
|
[ 'doBrightnessToggle', null, 'XF86KbdLightOnOff', false, ],
|
||||||
|
[ 'doInputSource', 'switch-input-source', null, false, [ +1 ] ],
|
||||||
|
[ 'doInputSource', 'switch-input-source-backward', null, false, [ -1 ] ],
|
||||||
|
[ 'doLaunchApp', null, 'XF86Battery', true, [ 'gnome-power-statistics.desktop' ] ]
|
||||||
|
];
|
||||||
|
|
||||||
|
var osdWin;
|
||||||
|
const OSDWindow = new Lang.Class({
|
||||||
|
Name: 'OSDWindow',
|
||||||
|
|
||||||
|
FADE_TIMEOUT: 1500,
|
||||||
|
FADE_DURATION: 100,
|
||||||
|
|
||||||
|
_init: function(iconName, value) {
|
||||||
|
/* assume 130x130 on a 640x480 display and scale from there */
|
||||||
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
|
let scalew = monitor.width / 640.0;
|
||||||
|
let scaleh = monitor.height / 480.0;
|
||||||
|
let scale = Math.min(scalew, scaleh);
|
||||||
|
let size = 130 * Math.max(1, scale);
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'osd-window',
|
||||||
|
vertical: true,
|
||||||
|
reactive: false,
|
||||||
|
visible: false,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._icon = new St.Icon({ icon_name: iconName,
|
||||||
|
icon_size: size / 2,
|
||||||
|
});
|
||||||
|
this.actor.add(this._icon, { expand: true,
|
||||||
|
x_align: St.Align.MIDDLE,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._value = value;
|
||||||
|
this._progressBar = new St.DrawingArea({ style_class: 'osd-progress-bar' });
|
||||||
|
this._progressBar.connect('repaint', Lang.bind(this, this._drawProgress));
|
||||||
|
this.actor.add(this._progressBar, { expand: true, x_fill: true, y_fill: false });
|
||||||
|
this._progressBar.visible = value !== undefined;
|
||||||
|
|
||||||
|
Main.layoutManager.addChrome(this.actor);
|
||||||
|
|
||||||
|
/* Position in the middle of primary monitor */
|
||||||
|
let [width, height] = this.actor.get_size();
|
||||||
|
this.actor.x = ((monitor.width - width) / 2) + monitor.x;
|
||||||
|
this.actor.y = monitor.y + (monitor.height / 2) + (monitor.height / 2 - height) / 2;
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
this.actor.show();
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: this.FADE_DURATION / 1000,
|
||||||
|
transition: 'easeInQuad' });
|
||||||
|
|
||||||
|
if (this._timeoutId)
|
||||||
|
GLib.source_remove(this._timeoutId);
|
||||||
|
|
||||||
|
this._timeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this.FADE_TIMEOUT, Lang.bind(this, this.hide));
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: this.FADE_DURATION / 1000,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
this.actor.destroy();
|
||||||
|
this.actor = null;
|
||||||
|
osdWin = null;
|
||||||
|
},
|
||||||
|
onCompleteScope: this });
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
setIcon: function(name) {
|
||||||
|
this._icon.icon_name = name;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
if (value == this._value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._value = value;
|
||||||
|
this._progressBar.visible = value !== undefined;
|
||||||
|
this._progressBar.queue_repaint();
|
||||||
|
},
|
||||||
|
|
||||||
|
_drawProgress: function(area) {
|
||||||
|
let cr = area.get_context();
|
||||||
|
|
||||||
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
let color = themeNode.get_foreground_color();
|
||||||
|
Clutter.cairo_set_source_color(cr, color);
|
||||||
|
|
||||||
|
let [width, height] = area.get_surface_size();
|
||||||
|
width = width * this._value;
|
||||||
|
|
||||||
|
cr.moveTo(0,0);
|
||||||
|
cr.lineTo(width, 0);
|
||||||
|
cr.lineTo(width, height);
|
||||||
|
cr.lineTo(0, height);
|
||||||
|
cr.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showOSD(icon, value) {
|
||||||
|
if (osdWin) {
|
||||||
|
osdWin.setIcon(icon);
|
||||||
|
osdWin.setValue(value);
|
||||||
|
} else {
|
||||||
|
osdWin = new OSDWindow(icon, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
osdWin.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MediaKeysGrabber = new Lang.Class({
|
||||||
|
Name: 'MediaKeysGrabber',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(MediaKeysInterface, this);
|
||||||
|
this._apps = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SettingsDaemon/MediaKeys');
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
this._dbusImpl.unexport();
|
||||||
|
},
|
||||||
|
|
||||||
|
GrabMediaPlayerKeysAsync: function(parameters, invocation) {
|
||||||
|
let [appName, time] = parameters;
|
||||||
|
|
||||||
|
/* I'm not sure of this code, but it is in gnome-settings-daemon
|
||||||
|
(letting alone that the introspection is wrong in glib...)
|
||||||
|
*/
|
||||||
|
if (time == Gdk.CURRENT_TIME) {
|
||||||
|
let tv = new GLib.TimeVal;
|
||||||
|
GLib.get_current_time(tv);
|
||||||
|
time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = -1;
|
||||||
|
for (let i = 0; i < this._apps.length; i++) {
|
||||||
|
if (this._apps[i].appName == appName) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != -1)
|
||||||
|
this._freeMediaPlayer(pos);
|
||||||
|
|
||||||
|
let app = {
|
||||||
|
appName: appName,
|
||||||
|
name: invocation.get_sender(),
|
||||||
|
time: time,
|
||||||
|
watchId: Gio.DBus.session.watch_name(invocation.get_sender(),
|
||||||
|
Gio.BusNameWatcherFlags.NONE,
|
||||||
|
null,
|
||||||
|
Lang.bind(this, this._onNameVanished)),
|
||||||
|
};
|
||||||
|
Util.insertSorted(this._apps, app, function(a, b) {
|
||||||
|
return b.time-a.time;
|
||||||
|
});
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('()', []));
|
||||||
|
},
|
||||||
|
|
||||||
|
ReleaseMediaPlayerAsync: function(parameters, invocation) {
|
||||||
|
let name = invocation.get_sender();
|
||||||
|
let [appName] = parameters;
|
||||||
|
|
||||||
|
let pos = -1;
|
||||||
|
for (let i = 0; i < this._apps.length; i++) {
|
||||||
|
if (this._apps[i].appName == appName) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == -1) {
|
||||||
|
for (let i = 0; i < this._apps.length; i++) {
|
||||||
|
if (this._apps[i].name == name) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != -1)
|
||||||
|
this._freeMediaPlayer(pos);
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('()', []));
|
||||||
|
},
|
||||||
|
|
||||||
|
_freeMediaPlayer: function(pos) {
|
||||||
|
let app = this._apps[pos];
|
||||||
|
Gio.bus_unwatch_name(app.watchId)
|
||||||
|
|
||||||
|
this._apps.splice(pos, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
mediaKeyPressed: function(key) {
|
||||||
|
if (this._apps.length == 0) {
|
||||||
|
showOSD('action-unavailable-symbolic');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = this._apps[0];
|
||||||
|
Gio.DBus.session.emit_signal(app.name,
|
||||||
|
'/org/gnome/SettingsDaemon/MediaKeys',
|
||||||
|
'org.gnome.SettingsDaemon.MediaKeys',
|
||||||
|
'MediaPlayerKeyPressed',
|
||||||
|
GLib.Variant.new('(ss)', [app.appName || '',
|
||||||
|
key]));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const MediaKeysManager = new Lang.Class({
|
||||||
|
Name: 'MediaKeysManager',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._a11yControl = Main.panel.statusArea.a11y;
|
||||||
|
this._volumeControl = Main.panel.statusArea.volume;
|
||||||
|
this._userMenu = Main.panel.statusArea.userMenu;
|
||||||
|
this._mediaPlayerKeys = new MediaKeysGrabber();
|
||||||
|
|
||||||
|
this._keybindingSettings = new Gio.Settings({ schema: KEYBINDING_SETTINGS });
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function() {
|
||||||
|
for (let i = 0; i < DEFAULT_KEYBINDINGS.length; i++) {
|
||||||
|
let [action, setting, keyval, overviewOnly, args] = DEFAULT_KEYBINDINGS[i];
|
||||||
|
let func = this[action];
|
||||||
|
if (!func) {
|
||||||
|
log('Keybinding action %s is missing'.format(action));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = setting ? setting : 'media-keys-keybindings-%d'.format(i);
|
||||||
|
let ok;
|
||||||
|
func = Util.wrapKeybinding(Lang.bind.apply(null, [this, func].concat(args)), overviewOnly);
|
||||||
|
if (setting)
|
||||||
|
ok = global.display.add_keybinding(setting, this._keybindingSettings,
|
||||||
|
Meta.KeyBindingFlags.BUILTIN |
|
||||||
|
Meta.KeyBindingFlags.IS_SINGLE |
|
||||||
|
Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func);
|
||||||
|
else
|
||||||
|
ok = global.display.add_grabbed_key(name, keyval,
|
||||||
|
Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
log('Installing keybinding %s failed'.format(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._customKeybindings = [];
|
||||||
|
this._changedId = this._keybindingSettings.connect('changed::custom-keybindings',
|
||||||
|
Lang.bind(this, this._reloadCustomKeybindings));
|
||||||
|
this._reloadCustomKeybindings();
|
||||||
|
|
||||||
|
this._mediaPlayerKeys.enable();
|
||||||
|
},
|
||||||
|
|
||||||
|
disable: function() {
|
||||||
|
for (let i = 0; i < DEFAULT_KEYBINDINGS.length; i++) {
|
||||||
|
let [action, setting, keyval, overviewOnly, args] = DEFAULT_KEYBINDINGS[i];
|
||||||
|
|
||||||
|
let name = setting ? setting : 'media-keys-keybindings-%d'.format(i);
|
||||||
|
if (setting)
|
||||||
|
global.display.remove_keybinding(setting, this._keybindingSettings);
|
||||||
|
else
|
||||||
|
global.display.remove_grabbed_key(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearCustomKeybindings();
|
||||||
|
this._keybindingSettings.disconnect(this._changedId);
|
||||||
|
|
||||||
|
this._mediaPlayerKeys.disable();
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearCustomKeybindings: function() {
|
||||||
|
for (let i = 0; i < this._customKeybindings.length; i++)
|
||||||
|
global.display.remove_keybinding('binding', this._customKeybindings[i]);
|
||||||
|
|
||||||
|
this._customKeybindings = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadCustomKeybindings: function() {
|
||||||
|
this._clearCustomKeybindings();
|
||||||
|
|
||||||
|
let paths = this._keybindingSettings.get_strv('custom-keybindings');
|
||||||
|
for (let i = 0; i < paths.length; i++) {
|
||||||
|
let setting = new Gio.Settings({ schema: CUSTOM_KEYBINDING_SETTINGS,
|
||||||
|
path: paths[i] });
|
||||||
|
let func = Util.wrapKeybinding(Lang.bind(this, this.doCustom, setting), true);
|
||||||
|
|
||||||
|
global.display.add_keybinding('binding', setting,
|
||||||
|
Meta.KeyBindingFlags.IS_SINGLE |
|
||||||
|
Meta.KeyBindingFlags.HANDLE_WHEN_GRABBED, func);
|
||||||
|
this._customKeybindings.push(setting);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
doCustom: function(display, screen, window, binding, settings) {
|
||||||
|
let command = settings.get_string('command');
|
||||||
|
Util.spawnCommandLine(command);
|
||||||
|
},
|
||||||
|
|
||||||
|
doTouchpadToggle: function(display, screen, window, binding) {
|
||||||
|
let settings = new Gio.Settings({ schema: TOUCHPAD_SETTINGS });
|
||||||
|
let enabled = settings.get_boolean('touchpad-enabled');
|
||||||
|
|
||||||
|
this.doTouchpadSet(display, screen, window, binding, !enabled);
|
||||||
|
settings.set_boolean(!enabled);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doTouchpadSet: function(display, screen, window, binding, enabled) {
|
||||||
|
showOSD(enabled ? 'input-touchpad-symbolic' : 'touchpad-disabled-symbolic');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doMute: function(display, screen, window, binding, quiet) {
|
||||||
|
let [icon, value] = this._volumeControl.volumeMenu.toggleMute(quiet);
|
||||||
|
showOSD(icon, value);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doVolumeAdjust: function(display, screen, window, binding, direction, quiet) {
|
||||||
|
let [icon, value] = this._volumeControl.volumeMenu.scroll(direction, quiet);
|
||||||
|
showOSD(icon, value);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doLogout: function(display, screen, window, binding) {
|
||||||
|
this._userMenu.logOut();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doEject: function(display, screen, window, binding) {
|
||||||
|
let volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
|
let drives = volumeMonitor.get_connected_drives();
|
||||||
|
let score = 0, drive;
|
||||||
|
for (let i = 0; i < drives.length; i++) {
|
||||||
|
if (!drives[i].can_eject())
|
||||||
|
continue;
|
||||||
|
if (!drives[i].is_media_removable())
|
||||||
|
continue;
|
||||||
|
if (score < 1) {
|
||||||
|
drive = drives[i];
|
||||||
|
score = 1;
|
||||||
|
}
|
||||||
|
if (!drives[i].has_media())
|
||||||
|
continue;
|
||||||
|
if (score < 2) {
|
||||||
|
drive = drives[i];
|
||||||
|
score = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showOSD('media-eject-custom-symbolic');
|
||||||
|
|
||||||
|
if (!drive)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
let mountOp = new ShellMountOperation.ShellMountOperation(drive);
|
||||||
|
drive.eject_with_operation(Gio.MountUnmountFlags.FORCE,
|
||||||
|
mountOp.mountOp, null, null);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doHome: function() {
|
||||||
|
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||||
|
let homeUri = homeFile.get_uri();
|
||||||
|
Gio.app_info_launch_default_for_uri(homeUri, null);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doLaunchMimeHandler: function(display, screen, window, binding, mimeType) {
|
||||||
|
let gioApp = Gio.AppInfo.get_default_for_type(mimeType, false);
|
||||||
|
if (gioApp != null) {
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_app(gioApp.get_id());
|
||||||
|
app.open_new_window(-1);
|
||||||
|
} else {
|
||||||
|
log('Could not find default application for \'%s\' mime-type'.format(mimeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doLaunchApp: function(display, screen, window, binding, appId) {
|
||||||
|
let app = Shell.AppSystem.get_default().lookup_app(appId);
|
||||||
|
app.open_new_window(-1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doScreensaver: function() {
|
||||||
|
// FIXME: handled in house, to the screenshield!
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doSpawn: function(display, screen, window, binding, argv) {
|
||||||
|
Util.spawn(argv);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
doMediaKey: function(display, screen, window, binding, key) {
|
||||||
|
this._mediaPlayerKeys.mediaKeyPressed(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onXRandRFinished: function(connection, result) {
|
||||||
|
connection.call_finish(result);
|
||||||
|
this._XRandRCancellable = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
doXRandRAction: function(display, screen, window, binding, action) {
|
||||||
|
if (this._XRandRCancellable)
|
||||||
|
this._XRandRCancellable.cancel();
|
||||||
|
|
||||||
|
this._XRandRCancellable = new Gio.Cancellable();
|
||||||
|
Gio.DBus.session.call('org.gnome.SettingsDaemon',
|
||||||
|
'/org/gnome/SettingsDaemon/XRANDR',
|
||||||
|
'org.gnome.SettingsDaemon.XRANDR_2',
|
||||||
|
action,
|
||||||
|
GLib.Variant.new('(x)', [global.get_current_time()]),
|
||||||
|
null, /* reply type */
|
||||||
|
Gio.DBusCallFlags.NONE,
|
||||||
|
-1,
|
||||||
|
this._XRandRCancellable,
|
||||||
|
Lang.bind(this, this._onXRandRFinished));
|
||||||
|
},
|
||||||
|
|
||||||
|
doA11yAction: function(display, screen, window, binding, key) {
|
||||||
|
let settings = new Gio.Settings({ schema: A11Y_SETTINGS });
|
||||||
|
let enabled = settings.get_boolean(key);
|
||||||
|
settings.set_boolean(key, !enabled);
|
||||||
|
},
|
||||||
|
|
||||||
|
doTextSize: function(display, screen, window, binding, multiplier) {
|
||||||
|
// Same values used in the Seeing tab of the Universal Access panel
|
||||||
|
const FACTORS = [ 0.75, 1.0, 1.25, 1.5 ];
|
||||||
|
|
||||||
|
// Figure out the current DPI scaling factor
|
||||||
|
let settings = new Gio.Settings({ schema: INTERFACE_SETTINGS });
|
||||||
|
let factor = settings.get_double('text-scaling-factor');
|
||||||
|
factor += multiplier * 0.25;
|
||||||
|
|
||||||
|
/* Try to find a matching value */
|
||||||
|
let distance = 1e6;
|
||||||
|
let best = 1.0;
|
||||||
|
for (let i = 0; i < FACTORS.length; i++) {
|
||||||
|
let d = Math.abs(factor - FACTORS[i]);
|
||||||
|
if (d < distance) {
|
||||||
|
best = factors[i];
|
||||||
|
distance = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best == 1.0)
|
||||||
|
settings.reset('text-scaling-factor');
|
||||||
|
else
|
||||||
|
settings.set_double('text-scaling-factor', best);
|
||||||
|
},
|
||||||
|
|
||||||
|
doToggleContrast: function(display, screen, window, binding) {
|
||||||
|
this._a11yControl.toggleHighContrast();
|
||||||
|
},
|
||||||
|
|
||||||
|
doMagnifierZoom: function(display, screen, window, binding, offset) {
|
||||||
|
let settings = new Gio.Settings({ schema: MAGNIFIER_SETTINGS });
|
||||||
|
|
||||||
|
let value = settings.get_value('mag-factor');
|
||||||
|
value = Math.round(value + offset);
|
||||||
|
settings.set_value('mag-factor', value);
|
||||||
|
},
|
||||||
|
|
||||||
|
doPowerAction: function(display, screen, window, binding, action) {
|
||||||
|
let settings = new Gio.Settings({ schema: POWER_SETTINGS });
|
||||||
|
switch (settings.get_string(action)) {
|
||||||
|
case 'suspend':
|
||||||
|
this._userMenu.suspend();
|
||||||
|
break;
|
||||||
|
case 'interactive':
|
||||||
|
case 'shutdown':
|
||||||
|
this._userMenu.shutdown();
|
||||||
|
break;
|
||||||
|
case 'hibernate':
|
||||||
|
this._userMenu.hibernate();
|
||||||
|
break;
|
||||||
|
case 'blank':
|
||||||
|
case 'default':
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onBrightnessFinished: function(connection, result, kind) {
|
||||||
|
let [percentage] = connection.call_finish(result).deep_unpack();
|
||||||
|
|
||||||
|
let icon = kind == 'Keyboard' ? 'keyboard-brightness-symbolic' : 'display-brightness-symbolic';
|
||||||
|
showOSD(icon, percentage / 100);
|
||||||
|
},
|
||||||
|
|
||||||
|
doBrightness: function(display, screen, window, binding, kind, action) {
|
||||||
|
let iface = 'org.gnome.SettingsDaemon.Power.' + kind;
|
||||||
|
let objectPath = '/org/gnome/SettingsDaemon/Power';
|
||||||
|
|
||||||
|
Gio.DBus.session.call('org.gnome.SettingsDaemon',
|
||||||
|
objectPath, iface, action,
|
||||||
|
null, null, /* parameters, reply type */
|
||||||
|
Gio.DBusCallFlags.NONE, -1, null,
|
||||||
|
Lang.bind(this, this._onBrightnessFinished, kind));
|
||||||
|
},
|
||||||
|
|
||||||
|
doInputSource: function(display, screen, window, binding, offset) {
|
||||||
|
let settings = new Gio.Settings({ schema: INPUT_SOURCE_SETTINGS });
|
||||||
|
|
||||||
|
let current = settings.get_uint('current');
|
||||||
|
let max = settings.get_strv('sources').length - 1;
|
||||||
|
|
||||||
|
current += offset;
|
||||||
|
if (current < 0)
|
||||||
|
current = 0;
|
||||||
|
else if (current > max)
|
||||||
|
current = max;
|
||||||
|
|
||||||
|
settings.set_uint('current', current);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Component = MediaKeysManager;
|
@ -37,7 +37,7 @@ const _modes = {
|
|||||||
isGreeter: true,
|
isGreeter: true,
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
unlockDialog: imports.gdm.loginDialog.LoginDialog,
|
||||||
components: ['polkitAgent'],
|
components: ['polkitAgent', 'mediaKeysManager'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['logo'],
|
left: ['logo'],
|
||||||
center: ['dateMenu'],
|
center: ['dateMenu'],
|
||||||
@ -50,7 +50,7 @@ const _modes = {
|
|||||||
isLocked: true,
|
isLocked: true,
|
||||||
isGreeter: undefined,
|
isGreeter: undefined,
|
||||||
unlockDialog: undefined,
|
unlockDialog: undefined,
|
||||||
components: ['polkitAgent', 'telepathyClient'],
|
components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['userMenu'],
|
left: ['userMenu'],
|
||||||
center: [],
|
center: [],
|
||||||
@ -61,7 +61,7 @@ const _modes = {
|
|||||||
'unlock-dialog': {
|
'unlock-dialog': {
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
unlockDialog: undefined,
|
unlockDialog: undefined,
|
||||||
components: ['polkitAgent', 'telepathyClient'],
|
components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['userMenu'],
|
left: ['userMenu'],
|
||||||
center: [],
|
center: [],
|
||||||
@ -71,7 +71,7 @@ const _modes = {
|
|||||||
|
|
||||||
'initial-setup': {
|
'initial-setup': {
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
components: ['keyring'],
|
components: ['keyring', 'mediaKeysManager'],
|
||||||
panel: {
|
panel: {
|
||||||
left: [],
|
left: [],
|
||||||
center: ['dateMenu'],
|
center: ['dateMenu'],
|
||||||
@ -91,8 +91,9 @@ const _modes = {
|
|||||||
isLocked: false,
|
isLocked: false,
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
||||||
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
|
components: ['networkAgent', 'polkitAgent', 'telepathyClient', 'keyring',
|
||||||
'keyring', 'recorder', 'autorunManager', 'automountManager'],
|
'recorder', 'autorunManager', 'automountManager',
|
||||||
|
'mediaKeysManager'],
|
||||||
panel: {
|
panel: {
|
||||||
left: ['activities', 'appMenu'],
|
left: ['activities', 'appMenu'],
|
||||||
center: ['dateMenu'],
|
center: ['dateMenu'],
|
||||||
|
@ -38,8 +38,8 @@ const ATIndicator = new Lang.Class({
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
|
this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
|
||||||
|
|
||||||
let highContrast = this._buildHCItem();
|
this._highContrast = this._buildHCItem();
|
||||||
this.menu.addMenuItem(highContrast);
|
this.menu.addMenuItem(this._highContrast);
|
||||||
|
|
||||||
let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
|
let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
|
||||||
'screen-magnifier-enabled');
|
'screen-magnifier-enabled');
|
||||||
@ -159,5 +159,9 @@ const ATIndicator = new Lang.Class({
|
|||||||
widget.setToggleState(active);
|
widget.setToggleState(active);
|
||||||
});
|
});
|
||||||
return widget;
|
return widget;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
toggleHighContrast: function() {
|
||||||
|
this._highContrast.toggle();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -66,7 +66,21 @@ const VolumeMenu = new Lang.Class({
|
|||||||
this._onControlStateChanged();
|
this._onControlStateChanged();
|
||||||
},
|
},
|
||||||
|
|
||||||
scroll: function(direction) {
|
toggleMute: function(quiet) {
|
||||||
|
let muted = this._output.is_muted;
|
||||||
|
this._output.change_is_muted(!muted);
|
||||||
|
|
||||||
|
if (muted && !quiet)
|
||||||
|
this._notifyVolumeChange();
|
||||||
|
|
||||||
|
if (!muted)
|
||||||
|
return ['audio-volume-muted-symbolic', 0];
|
||||||
|
else
|
||||||
|
return [this._volumeToIcon(this._output.volume),
|
||||||
|
this._output.volume / this._volumeMax];
|
||||||
|
},
|
||||||
|
|
||||||
|
scroll: function(direction, quiet) {
|
||||||
let currentVolume = this._output.volume;
|
let currentVolume = this._output.volume;
|
||||||
|
|
||||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||||
@ -85,7 +99,14 @@ const VolumeMenu = new Lang.Class({
|
|||||||
this._output.push_volume();
|
this._output.push_volume();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._notifyVolumeChange();
|
if (!quiet)
|
||||||
|
this._notifyVolumeChange();
|
||||||
|
|
||||||
|
if (this._output.is_muted)
|
||||||
|
return ['audio-volume-muted-symbolic', 0];
|
||||||
|
else
|
||||||
|
return [this._volumeToIcon(this._output.volume),
|
||||||
|
this._output.volume / this._volumeMax];
|
||||||
},
|
},
|
||||||
|
|
||||||
_onControlStateChanged: function() {
|
_onControlStateChanged: function() {
|
||||||
@ -221,14 +242,14 @@ const Indicator = new Lang.Class({
|
|||||||
this.parent('audio-volume-muted-symbolic', _("Volume"));
|
this.parent('audio-volume-muted-symbolic', _("Volume"));
|
||||||
|
|
||||||
this._control = getMixerControl();
|
this._control = getMixerControl();
|
||||||
this._volumeMenu = new VolumeMenu(this._control);
|
this.volumeMenu = new VolumeMenu(this._control);
|
||||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
this.volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
||||||
this._hasPulseAudio = (icon != null);
|
this._hasPulseAudio = (icon != null);
|
||||||
this.setIcon(icon);
|
this.setIcon(icon);
|
||||||
this._syncVisibility();
|
this._syncVisibility();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.menu.addMenuItem(this._volumeMenu);
|
this.menu.addMenuItem(this.volumeMenu);
|
||||||
|
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
|
||||||
@ -242,6 +263,6 @@ const Indicator = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onScrollEvent: function(actor, event) {
|
_onScrollEvent: function(actor, event) {
|
||||||
this._volumeMenu.scroll(event.get_scroll_direction());
|
this.volumeMenu.scroll(event.get_scroll_direction(), false);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
@ -572,6 +572,7 @@ const UserMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
this._updateHaveShutdown();
|
this._updateHaveShutdown();
|
||||||
this._updateHaveSuspend();
|
this._updateHaveSuspend();
|
||||||
|
this._updateHaveHibernate();
|
||||||
}));
|
}));
|
||||||
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
|
||||||
Lang.bind(this, this._updateHaveShutdown));
|
Lang.bind(this, this._updateHaveShutdown));
|
||||||
@ -655,6 +656,13 @@ const UserMenuButton = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateHaveHibernate: function() {
|
||||||
|
this._loginManager.canHibernate(Lang.bind(this,
|
||||||
|
function(result) {
|
||||||
|
this._haveHibernate = result;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
_updateSuspendOrPowerOff: function() {
|
_updateSuspendOrPowerOff: function() {
|
||||||
if (!this._suspendOrPowerOffItem)
|
if (!this._suspendOrPowerOffItem)
|
||||||
return;
|
return;
|
||||||
@ -766,7 +774,7 @@ const UserMenuButton = new Lang.Class({
|
|||||||
this._loginScreenItem = item;
|
this._loginScreenItem = item;
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
item = new PopupMenu.PopupMenuItem(_("Log Out"));
|
||||||
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
item.connect('activate', Lang.bind(this, this.logOut));
|
||||||
this.menu.addMenuItem(item);
|
this.menu.addMenuItem(item);
|
||||||
this._logoutItem = item;
|
this._logoutItem = item;
|
||||||
|
|
||||||
@ -835,7 +843,7 @@ const UserMenuButton = new Lang.Class({
|
|||||||
Gdm.goto_login_session_sync(null);
|
Gdm.goto_login_session_sync(null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onQuitSessionActivate: function() {
|
logOut: function() {
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
this._session.LogoutRemote(0);
|
this._session.LogoutRemote(0);
|
||||||
},
|
},
|
||||||
@ -847,25 +855,60 @@ const UserMenuButton = new Lang.Class({
|
|||||||
this._session.RebootRemote();
|
this._session.RebootRemote();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shutdown: function() {
|
||||||
|
this._session.ShutdownRemote();
|
||||||
|
},
|
||||||
|
|
||||||
|
suspend: function() {
|
||||||
|
if (!this._haveSuspend)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure we only suspend after locking the screen
|
||||||
|
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||||
|
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||||
|
Main.screenShield.disconnect(tmpId);
|
||||||
|
|
||||||
|
this._loginManager.suspend();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||||
|
Main.screenShield.lock(true);
|
||||||
|
} else {
|
||||||
|
this._loginManager.suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
hibernate: function() {
|
||||||
|
if (!this._haveHibernate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure we only suspend after locking the screen
|
||||||
|
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||||
|
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||||
|
Main.screenShield.disconnect(tmpId);
|
||||||
|
|
||||||
|
this._loginManager.hibernate();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||||
|
Main.screenShield.lock(true);
|
||||||
|
} else {
|
||||||
|
this._loginManager.hibernate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
_onSuspendOrPowerOffActivate: function() {
|
_onSuspendOrPowerOffActivate: function() {
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
|
|
||||||
if (this._haveShutdown &&
|
if (this._haveShutdown &&
|
||||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||||
this._session.ShutdownRemote();
|
this.shutdown();
|
||||||
} else {
|
} else {
|
||||||
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
|
this.suspend();
|
||||||
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
|
||||||
Main.screenShield.disconnect(tmpId);
|
|
||||||
|
|
||||||
this._loginManager.suspend();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
|
||||||
Main.screenShield.lock(true);
|
|
||||||
} else {
|
|
||||||
this._loginManager.suspend();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user