diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 58035d303..22ba047d8 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -131,6 +131,7 @@
ui/status/rfkill.js
ui/status/volume.js
ui/status/bluetooth.js
+ ui/status/remoteAccess.js
ui/status/screencast.js
ui/status/system.js
ui/status/thunderbolt.js
diff --git a/js/ui/panel.js b/js/ui/panel.js
index d1a572503..7a136fec5 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -716,6 +716,7 @@ var AggregateMenu = new Lang.Class({
this._bluetooth = null;
}
+ this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet();
this._power = new imports.ui.status.power.Indicator();
this._rfkill = new imports.ui.status.rfkill.Indicator();
this._volume = new imports.ui.status.volume.Indicator();
@@ -736,6 +737,7 @@ var AggregateMenu = new Lang.Class({
if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators);
}
+ this._indicators.add_child(this._remoteAccess.indicators);
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
@@ -750,6 +752,7 @@ var AggregateMenu = new Lang.Class({
if (this._bluetooth) {
this.menu.addMenuItem(this._bluetooth.menu);
}
+ this.menu.addMenuItem(this._remoteAccess.menu);
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js
new file mode 100644
index 000000000..ffa334001
--- /dev/null
+++ b/js/ui/status/remoteAccess.js
@@ -0,0 +1,80 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+
+var RemoteAccessApplet = new Lang.Class({
+ Name: 'RemoteAccessApplet',
+ Extends: PanelMenu.SystemIndicator,
+
+ _init() {
+ this.parent();
+
+ let backend = Meta.get_backend();
+ let controller = backend.get_remote_access_controller();
+
+ if (!controller)
+ return;
+
+ // We can't possibly know about all types of screen sharing on X11, so
+ // showing these controls on X11 might give a false sense of security.
+ // Thus, only enable these controls when using Wayland, where we are
+ // in control of sharing.
+ if (!Meta.is_wayland_compositor())
+ return;
+
+ this._handles = new Set();
+ this._indicator = null;
+ this._menuSection = null;
+
+ controller.connect('new-handle', (controller, handle) => {
+ this._onNewHandle(handle);
+ });
+ },
+
+ _ensureControls() {
+ if (this._indicator)
+ return;
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'screen-shared-symbolic';
+ this._item =
+ new PopupMenu.PopupSubMenuMenuItem(_("Screen is Being Shared"),
+ true);
+ this._item.menu.addAction(_("Turn off"),
+ () => {
+ for (let handle of this._handles)
+ handle.stop();
+ });
+ this._item.icon.icon_name = 'screen-shared-symbolic';
+ this.menu.addMenuItem(this._item);
+ },
+
+ _sync() {
+ if (this._handles.size == 0) {
+ this._indicator.visible = false;
+ this._item.actor.visible = false;
+ } else {
+ this._indicator.visible = true;
+ this._item.actor.visible = true;
+ }
+ },
+
+ _onStopped(handle) {
+ this._handles.delete(handle);
+ this._sync();
+ },
+
+ _onNewHandle(handle) {
+ this._handles.add(handle);
+ handle.connect('stopped', this._onStopped.bind(this));
+
+ if (this._handles.size == 1) {
+ this._ensureControls();
+ this._sync();
+ }
+ },
+});