diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 7beede392..dc9a006b1 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1812,6 +1812,9 @@ StScrollBar StButton#vhandle:active {
text-align: center;
font-weight: bold;
spacing: 1em;
+ margin: 32px;
+ min-width: 64px;
+ min-height: 64px;
}
.osd-window .level {
@@ -1820,6 +1823,10 @@ StScrollBar StButton#vhandle:active {
background-color: rgba(190,190,190,0.2);
}
+.osd-monitor-label {
+ font-size: 3em;
+}
+
/* App Switcher */
.switcher-popup {
padding: 8px;
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 02d2dd001..439c0aad9 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -64,6 +64,7 @@
ui/modalDialog.js
ui/notificationDaemon.js
ui/osdWindow.js
+ ui/osdMonitorLabeler.js
ui/overview.js
ui/overviewControls.js
ui/panel.js
diff --git a/js/ui/main.js b/js/ui/main.js
index 32c864475..aecf5fbca 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -21,6 +21,7 @@ const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const OsdWindow = imports.ui.osdWindow;
+const OsdMonitorLabeler = imports.ui.osdMonitorLabeler;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
@@ -57,6 +58,7 @@ let notificationDaemon = null;
let windowAttentionHandler = null;
let ctrlAltTabManager = null;
let osdWindowManager = null;
+let osdMonitorLabeler = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
@@ -150,6 +152,7 @@ function _initializeUI() {
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindowManager = new OsdWindow.OsdWindowManager();
+ osdMonitorLabeler = new OsdMonitorLabeler.OsdMonitorLabeler();
overview = new Overview.Overview();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
diff --git a/js/ui/osdMonitorLabeler.js b/js/ui/osdMonitorLabeler.js
new file mode 100644
index 000000000..9d519f501
--- /dev/null
+++ b/js/ui/osdMonitorLabeler.js
@@ -0,0 +1,141 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const Gio = imports.gi.Gio;
+const St = imports.gi.St;
+
+const Lang = imports.lang;
+const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
+const Meta = imports.gi.Meta;
+
+const FADE_TIME = 0.1;
+
+const OsdMonitorLabel = new Lang.Class({
+ Name: 'OsdMonitorLabel',
+
+ _init: function(monitor, label) {
+ this._actor = new St.Widget({ opacity: 0,
+ x_expand: true,
+ y_expand: true });
+
+ this._monitor = monitor;
+
+ this._box = new St.BoxLayout({ style_class: 'osd-window',
+ vertical: true });
+ this._actor.add_actor(this._box);
+
+ this._label = new St.Label({ style_class: 'osd-monitor-label',
+ text: label });
+ this._box.add(this._label);
+
+ Main.uiGroup.add_child(this._actor);
+ Main.uiGroup.set_child_above_sibling(this._actor, null);
+ this._position();
+
+ Meta.disable_unredirect_for_screen(global.screen);
+ Tweener.addTween(this._actor,
+ { opacity: 255,
+ time: FADE_TIME,
+ transition: 'easeOutQuad' });
+ },
+
+ _position: function() {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor);
+
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+ this._box.x = workArea.x + (workArea.width - this._box.width);
+ else
+ this._box.x = workArea.x;
+
+ this._box.y = workArea.y;
+ },
+
+ destroy: function() {
+ Tweener.addTween(this._actor,
+ { opacity: 0,
+ time: FADE_TIME,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this, function() {
+ this._actor.destroy();
+ Meta.enable_unredirect_for_screen(global.screen);
+ })
+ });
+ }
+});
+
+const OsdMonitorLabeler = new Lang.Class({
+ Name: 'OsdMonitorLabeler',
+
+ _init: function() {
+ this._monitorManager = Meta.MonitorManager.get();
+ this._client = null;
+ this._clientWatchId = 0;
+ this._osdLabels = [];
+ this._monitorLabels = null;
+ Main.layoutManager.connect('monitors-changed',
+ Lang.bind(this, this._reset));
+ this._reset();
+ },
+
+ _reset: function() {
+ for (let i in this._osdLabels)
+ this._osdLabels[i].destroy();
+ this._osdLabels = [];
+ this._monitorLabels = new Map();
+ let monitors = Main.layoutManager.monitors;
+ for (let i in monitors)
+ this._monitorLabels.set(monitors[i].index, []);
+ },
+
+ _trackClient: function(client) {
+ if (this._client)
+ return (this._client == client);
+
+ this._client = client;
+ this._clientWatchId = Gio.bus_watch_name(Gio.BusType.SESSION, client, 0, null,
+ Lang.bind(this, function(c, name) {
+ this.hide(name);
+ }));
+ return true;
+ },
+
+ _untrackClient: function(client) {
+ if (!this._client || this._client != client)
+ return false;
+
+ Gio.bus_unwatch_name(this._clientWatchId);
+ this._clientWatchId = 0;
+ this._client = null;
+ return true;
+ },
+
+ show: function(client, params) {
+ if (!this._trackClient(client))
+ return;
+
+ this._reset();
+
+ for (let id in params) {
+ let monitor = this._monitorManager.get_monitor_for_output(id);
+ if (monitor == -1)
+ continue;
+ this._monitorLabels.get(monitor).push(params[id].deep_unpack());
+ }
+
+ // In mirrored display setups, more than one physical outputs
+ // might be showing the same logical monitor. In that case, we
+ // join each output's labels on the same OSD widget.
+ for (let [monitor, labels] of this._monitorLabels.entries()) {
+ labels.sort();
+ this._osdLabels.push(new OsdMonitorLabel(monitor, labels.join(' ')));
+ }
+ },
+
+ hide: function(client) {
+ if (!this._untrackClient(client))
+ return;
+
+ this._reset();
+ }
+});
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index 7e45d84ad..14dba7292 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -25,6 +25,10 @@ const GnomeShellIface = ' \
\
\
\
+ \
+ \
+ \
+ \
\
\
\
@@ -240,6 +244,17 @@ const GnomeShell = new Lang.Class({
this._grabbers.delete(name);
},
+ ShowMonitorLabelsAsync: function(params, invocation) {
+ let sender = invocation.get_sender();
+ let [dict] = params;
+ Main.osdMonitorLabeler.show(sender, dict);
+ },
+
+ HideMonitorLabelsAsync: function(params, invocation) {
+ let sender = invocation.get_sender();
+ Main.osdMonitorLabeler.hide(sender);
+ },
+
Mode: global.session_mode,