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,