Compare commits
	
		
			3 Commits
		
	
	
		
			wip/rstrod
			...
			wip/media-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 60d87ef4ba | ||
|   | d886dc17e1 | ||
|   | 9dd2a467ba | 
| @@ -2325,3 +2325,17 @@ StScrollBar StButton#vhandle:active { | ||||
|     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/autorunManager.js		\ | ||||
| 	ui/components/automountManager.js	\ | ||||
| 	ui/components/mediaKeysManager.js	\ | ||||
| 	ui/components/networkAgent.js		\ | ||||
| 	ui/components/polkitAgent.js		\ | ||||
| 	ui/components/recorder.js		\ | ||||
|   | ||||
| @@ -17,6 +17,9 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager | ||||
| <method name='Suspend'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='Hibernate'> | ||||
|     <arg type='b' direction='in'/> | ||||
| </method> | ||||
| <method name='CanPowerOff'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| @@ -26,6 +29,9 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager | ||||
| <method name='CanSuspend'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| <method name='CanHibernate'> | ||||
|     <arg type='s' direction='out'/> | ||||
| </method> | ||||
| </interface>; | ||||
|  | ||||
| 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() { | ||||
|         this._proxy.PowerOffRemote(true); | ||||
|     }, | ||||
| @@ -150,6 +165,10 @@ const LoginManagerSystemd = new Lang.Class({ | ||||
|  | ||||
|     suspend: function() { | ||||
|         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() { | ||||
|         this._proxy.StopRemote(); | ||||
|     }, | ||||
| @@ -225,5 +251,9 @@ const LoginManagerConsoleKit = new Lang.Class({ | ||||
|  | ||||
|     suspend: function() { | ||||
|         this._upClient.suspend_sync(null); | ||||
|     }, | ||||
|  | ||||
|     hibernate: function() { | ||||
|         this._upClient.hibernate_sync(null); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -288,3 +288,31 @@ function insertSorted(array, val, cmp) { | ||||
|  | ||||
|     return pos; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * wrapKeybinding: | ||||
|  * | ||||
|  * Wrap a keybinding handler so that | ||||
|  * it ignores an invocation if the shell is modal, but | ||||
|  * not when the overview is active, or when global | ||||
|  * keybindings are allowed by session mode. | ||||
|  * This function is only useful for keybindings installed | ||||
|  * with Meta.KeybindingFlags.HANDLE_WHEN_GRABBED | ||||
|  */ | ||||
| function wrapKeybinding(handler, onlyInOverview) { | ||||
|     return function() { | ||||
|         let handle; | ||||
|  | ||||
|         if (onlyInOverview) { | ||||
|             handle = Main.sessionMode.allowKeybindingsWhenModal || | ||||
|                 Main.modalCount == (Main.overview.visible ? 1 : 0); | ||||
|         } else { | ||||
|             handle = true; | ||||
|         } | ||||
|  | ||||
|         if (handle) | ||||
|             return handler.apply(this, arguments); | ||||
|         else | ||||
|             return false; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -199,7 +199,7 @@ const AltTabPopup = new Lang.Class({ | ||||
|         this.actor.get_allocation_box(); | ||||
|  | ||||
|         // Make the initial selection | ||||
|         if (binding == 'switch-group') { | ||||
|         if (binding == 'internal-keybinding-switch-group') { | ||||
|             if (backward) { | ||||
|                 this._select(0, this._appIcons[0].cachedWindows.length - 1); | ||||
|             } else { | ||||
| @@ -208,9 +208,9 @@ const AltTabPopup = new Lang.Class({ | ||||
|                 else | ||||
|                     this._select(0, 0); | ||||
|             } | ||||
|         } else if (binding == 'switch-group-backward') { | ||||
|         } else if (binding == 'internal-keybinding-switch-group-backward') { | ||||
|             this._select(0, this._appIcons[0].cachedWindows.length - 1); | ||||
|         } else if (binding == 'switch-windows-backward') { | ||||
|         } else if (binding == 'internal-keybinding-switch-windows-backward') { | ||||
|             this._select(this._appIcons.length - 1); | ||||
|         } else if (this._appIcons.length == 1) { | ||||
|             this._select(0); | ||||
|   | ||||
							
								
								
									
										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; | ||||
| @@ -52,6 +52,8 @@ const Recorder = new Lang.Class({ | ||||
|             Meta.disable_unredirect_for_screen(global.screen); | ||||
|             recorder.record(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -69,7 +69,7 @@ let _overridesSettings = null; | ||||
| let background = null; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null); | ||||
|     Meta.keybindings_set_custom_handler('internal-keybinding-panel-run-dialog', sessionMode.hasRunDialog ? Util.wrapKeybinding(openRunDialog, true) : null); | ||||
|     if (sessionMode.isGreeter) | ||||
|         screenShield.showDialog(); | ||||
| } | ||||
| @@ -155,8 +155,14 @@ function start() { | ||||
|  | ||||
|     global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, | ||||
|                                             false, -1, 1); | ||||
|     Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle)); | ||||
|     global.display.connect('overlay-key', Lang.bind(overview, overview.toggle)); | ||||
|     Meta.keybindings_set_custom_handler('internal-keybinding-panel-main-menu', Util.wrapKeybinding(Lang.bind(overview, function() { | ||||
|         this.toggle(); | ||||
|         return true; | ||||
|     }), true)); | ||||
|     global.display.connect('overlay-key', Util.wrapKeybinding(Lang.bind(overview, function() { | ||||
|         this.toggle(); | ||||
|         return true; | ||||
|     }), true)); | ||||
|  | ||||
|     sessionMode.connect('update', _sessionUpdated); | ||||
|     _sessionUpdated(); | ||||
| @@ -167,8 +173,6 @@ function start() { | ||||
|  | ||||
|     _startDate = new Date(); | ||||
|  | ||||
|     global.stage.connect('captured-event', _globalKeyPressHandler); | ||||
|  | ||||
|     log('GNOME Shell started at ' + _startDate); | ||||
|  | ||||
|     let perfModuleName = GLib.getenv("SHELL_PERF_MODULE"); | ||||
| @@ -465,86 +469,6 @@ function getWindowActorsForWorkspace(workspaceIndex) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| // This function encapsulates hacks to make certain global keybindings | ||||
| // work even when we are in one of our modes where global keybindings | ||||
| // are disabled with a global grab. (When there is a global grab, then | ||||
| // all key events will be delivered to the stage, so ::captured-event | ||||
| // on the stage can be used for global keybindings.) | ||||
| function _globalKeyPressHandler(actor, event) { | ||||
|     if (modalCount == 0) | ||||
|         return false; | ||||
|     if (event.type() != Clutter.EventType.KEY_PRESS && event.type() != Clutter.EventType.KEY_RELEASE) | ||||
|         return false; | ||||
|  | ||||
|     if (!sessionMode.allowKeybindingsWhenModal) { | ||||
|         if (modalCount > (overview.visible ? 1 : 0)) | ||||
|             return false; | ||||
|     } | ||||
|  | ||||
|     let symbol = event.get_key_symbol(); | ||||
|     let keyCode = event.get_key_code(); | ||||
|     let ignoredModifiers = global.display.get_ignored_modifier_mask(); | ||||
|     let modifierState = event.get_state() & ~ignoredModifiers; | ||||
|  | ||||
|     // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType | ||||
|     let action = global.display.get_keybinding_action(keyCode, modifierState); | ||||
|  | ||||
|     if (event.type() == Clutter.EventType.KEY_PRESS) { | ||||
|         if (action == Meta.KeyBindingAction.SWITCH_PANELS) { | ||||
|             ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK, | ||||
|                                     modifierState); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         switch (action) { | ||||
|             // left/right would effectively act as synonyms for up/down if we enabled them; | ||||
|             // but that could be considered confusing; we also disable them in the main view. | ||||
|             // | ||||
|             // case Meta.KeyBindingAction.WORKSPACE_LEFT: | ||||
|             //  if (!sessionMode.hasWorkspaces) | ||||
|             //      return false; | ||||
|             // | ||||
|             //     wm.actionMoveWorkspaceLeft(); | ||||
|             //     return true; | ||||
|             // case Meta.KeyBindingAction.WORKSPACE_RIGHT: | ||||
|             //  if (!sessionMode.hasWorkspaces) | ||||
|             //      return false; | ||||
|             // | ||||
|             //     wm.actionMoveWorkspaceRight(); | ||||
|             //     return true; | ||||
|             case Meta.KeyBindingAction.WORKSPACE_UP: | ||||
|                 if (!sessionMode.hasWorkspaces) | ||||
|                     return false; | ||||
|  | ||||
|                 wm.actionMoveWorkspace(Meta.MotionDirection.UP); | ||||
|                 return true; | ||||
|             case Meta.KeyBindingAction.WORKSPACE_DOWN: | ||||
|                 if (!sessionMode.hasWorkspaces) | ||||
|                     return false; | ||||
|  | ||||
|                 wm.actionMoveWorkspace(Meta.MotionDirection.DOWN); | ||||
|                 return true; | ||||
|             case Meta.KeyBindingAction.PANEL_RUN_DIALOG: | ||||
|             case Meta.KeyBindingAction.COMMAND_2: | ||||
|                 if (!sessionMode.hasRunDialog) | ||||
|                     return false; | ||||
|  | ||||
|                 openRunDialog(); | ||||
|                 return true; | ||||
|             case Meta.KeyBindingAction.PANEL_MAIN_MENU: | ||||
|                 overview.hide(); | ||||
|                 return true; | ||||
|         } | ||||
|     } else if (event.type() == Clutter.EventType.KEY_RELEASE) { | ||||
|         if (action == Meta.KeyBindingAction.OVERLAY_KEY) { | ||||
|             overview.hide(); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function _findModal(actor) { | ||||
|     for (let i = 0; i < modalActorFocusStack.length; i++) { | ||||
|         if (modalActorFocusStack[i].actor == actor) | ||||
| @@ -685,6 +609,8 @@ function openRunDialog() { | ||||
|         runDialog = new RunDialog.RunDialog(); | ||||
|     } | ||||
|     runDialog.open(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -37,7 +37,7 @@ const _modes = { | ||||
|         isGreeter: true, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.gdm.loginDialog.LoginDialog, | ||||
|         components: ['polkitAgent'], | ||||
|         components: ['polkitAgent', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['logo'], | ||||
|             center: ['dateMenu'], | ||||
| @@ -50,7 +50,7 @@ const _modes = { | ||||
|         isLocked: true, | ||||
|         isGreeter: undefined, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient'], | ||||
|         components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
| @@ -61,7 +61,7 @@ const _modes = { | ||||
|     'unlock-dialog': { | ||||
|         isLocked: true, | ||||
|         unlockDialog: undefined, | ||||
|         components: ['polkitAgent', 'telepathyClient'], | ||||
|         components: ['polkitAgent', 'telepathyClient', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['userMenu'], | ||||
|             center: [], | ||||
| @@ -71,7 +71,7 @@ const _modes = { | ||||
|  | ||||
|     'initial-setup': { | ||||
|         isPrimary: true, | ||||
|         components: ['keyring'], | ||||
|         components: ['keyring', 'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: [], | ||||
|             center: ['dateMenu'], | ||||
| @@ -91,8 +91,9 @@ const _modes = { | ||||
|         isLocked: false, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.ui.unlockDialog.UnlockDialog, | ||||
|         components: ['networkAgent', 'polkitAgent', 'telepathyClient', | ||||
|                      'keyring', 'recorder', 'autorunManager', 'automountManager'], | ||||
|         components: ['networkAgent', 'polkitAgent', 'telepathyClient', 'keyring', | ||||
|                      'recorder', 'autorunManager', 'automountManager', | ||||
|                      'mediaKeysManager'], | ||||
|         panel: { | ||||
|             left: ['activities', 'appMenu'], | ||||
|             center: ['dateMenu'], | ||||
|   | ||||
| @@ -38,8 +38,8 @@ const ATIndicator = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility")); | ||||
|  | ||||
|         let highContrast = this._buildHCItem(); | ||||
|         this.menu.addMenuItem(highContrast); | ||||
|         this._highContrast = this._buildHCItem(); | ||||
|         this.menu.addMenuItem(this._highContrast); | ||||
|  | ||||
|         let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA, | ||||
|                                                    'screen-magnifier-enabled'); | ||||
| @@ -159,5 +159,9 @@ const ATIndicator = new Lang.Class({ | ||||
|             widget.setToggleState(active); | ||||
|         }); | ||||
|         return widget; | ||||
|     } | ||||
|     }, | ||||
|  | ||||
|     toggleHighContrast: function() { | ||||
|         this._highContrast.toggle(); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -66,7 +66,21 @@ const VolumeMenu = new Lang.Class({ | ||||
|         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; | ||||
|  | ||||
|         if (direction == Clutter.ScrollDirection.DOWN) { | ||||
| @@ -85,7 +99,14 @@ const VolumeMenu = new Lang.Class({ | ||||
|             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() { | ||||
| @@ -221,14 +242,14 @@ const Indicator = new Lang.Class({ | ||||
|         this.parent('audio-volume-muted-symbolic', _("Volume")); | ||||
|  | ||||
|         this._control = getMixerControl(); | ||||
|         this._volumeMenu = new VolumeMenu(this._control); | ||||
|         this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) { | ||||
|         this.volumeMenu = new VolumeMenu(this._control); | ||||
|         this.volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) { | ||||
|             this._hasPulseAudio = (icon != null); | ||||
|             this.setIcon(icon); | ||||
|             this._syncVisibility(); | ||||
|         })); | ||||
|  | ||||
|         this.menu.addMenuItem(this._volumeMenu); | ||||
|         this.menu.addMenuItem(this.volumeMenu); | ||||
|  | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); | ||||
| @@ -242,6 +263,6 @@ const Indicator = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _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._updateHaveSuspend(); | ||||
|                 this._updateHaveHibernate(); | ||||
|             })); | ||||
|         this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY, | ||||
|                                        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() { | ||||
|         if (!this._suspendOrPowerOffItem) | ||||
|             return; | ||||
| @@ -766,7 +774,7 @@ const UserMenuButton = new Lang.Class({ | ||||
|         this._loginScreenItem = item; | ||||
|  | ||||
|         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._logoutItem = item; | ||||
|  | ||||
| @@ -835,7 +843,7 @@ const UserMenuButton = new Lang.Class({ | ||||
|         Gdm.goto_login_session_sync(null); | ||||
|     }, | ||||
|  | ||||
|     _onQuitSessionActivate: function() { | ||||
|     logOut: function() { | ||||
|         Main.overview.hide(); | ||||
|         this._session.LogoutRemote(0); | ||||
|     }, | ||||
| @@ -847,25 +855,60 @@ const UserMenuButton = new Lang.Class({ | ||||
|         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() { | ||||
|         Main.overview.hide(); | ||||
|  | ||||
|         if (this._haveShutdown && | ||||
|             this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) { | ||||
|             this._session.ShutdownRemote(); | ||||
|             this.shutdown(); | ||||
|         } else { | ||||
|             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(); | ||||
|             } | ||||
|             this.suspend(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -12,6 +12,7 @@ const AltTab = imports.ui.altTab; | ||||
| const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const WINDOW_ANIMATION_TIME = 0.25; | ||||
| @@ -101,32 +102,32 @@ const WindowManager = new Lang.Class({ | ||||
|         this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); | ||||
|  | ||||
|         this._workspaceSwitcherPopup = null; | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-left', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-left', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-right', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-up', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-to-workspace-down', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._showWorkspaceSwitcher), true)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-left', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-right', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-right', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-up', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-up', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-to-workspace-down', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-move-to-workspace-down', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-left', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-right', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-up', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('move-to-workspace-down', | ||||
|                                             Lang.bind(this, this._showWorkspaceSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-windows', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-windows', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-group', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-group', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-windows-backward', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-windows-backward', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-group-backward', | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-group-backward', | ||||
|                                             Lang.bind(this, this._startAppSwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('switch-panels', | ||||
|                                             Lang.bind(this, this._startA11ySwitcher)); | ||||
|         Meta.keybindings_set_custom_handler('internal-keybinding-switch-panels', | ||||
|                                             Util.wrapKeybinding(Lang.bind(this, this._startA11ySwitcher), true)); | ||||
|         global.display.add_keybinding('open-application-menu', | ||||
|                                       new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                                       Meta.KeyBindingFlags.NONE, | ||||
| @@ -552,30 +553,32 @@ const WindowManager = new Lang.Class({ | ||||
|         let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK; | ||||
|         if (!tabPopup.show(backwards, binding.get_name(), binding.get_mask())) | ||||
|             tabPopup.destroy(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _startA11ySwitcher : function(display, screen, window, binding) { | ||||
|         let modifiers = binding.get_modifiers(); | ||||
|         let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK; | ||||
|         Main.ctrlAltTabManager.popup(backwards, binding.get_mask()); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _openAppMenu : function(display, screen, window, event, binding) { | ||||
|         Main.panel.openAppMenu(); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _showWorkspaceSwitcher : function(display, screen, window, binding) { | ||||
|         if (screen.n_workspaces == 1) | ||||
|             return; | ||||
|             return false; | ||||
|  | ||||
|         let [action,,,direction] = binding.get_name().split('-'); | ||||
|         let [,,action,,,direction] = binding.get_name().split('-'); | ||||
|         let direction = Meta.MotionDirection[direction.toUpperCase()]; | ||||
|         let newWs; | ||||
|  | ||||
|  | ||||
|         if (direction != Meta.MotionDirection.UP && | ||||
|             direction != Meta.MotionDirection.DOWN) | ||||
|             return; | ||||
|             return false; | ||||
|  | ||||
|         if (action == 'switch') | ||||
|             newWs = this.actionMoveWorkspace(direction); | ||||
| @@ -591,6 +594,8 @@ const WindowManager = new Lang.Class({ | ||||
|             } | ||||
|             this._workspaceSwitcherPopup.display(direction, newWs.index()); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     actionMoveWorkspace: function(direction) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user