From 19004688463efe2d2e6bd942f60ce7307edf5168 Mon Sep 17 00:00:00 2001 From: Rui Matos Date: Mon, 26 Jan 2015 15:52:10 +0100 Subject: [PATCH] Add an OSD monitor labeler exposed on DBus This DBus API is intended to be used by gnome-control-center's displays panel to show monitor labels. Each output (i.e. hardware monitor) identified by its org.gnome.Mutter.DisplayConfig API ID has at most one label. On mirrored setups, all the labels for outputs corresponding to the same logical monitor (i.e. showing the same contents in the same mode) are shown together. At most, only one DBus client at a time is allowed to show labels. https://bugzilla.gnome.org/show_bug.cgi?id=743744 --- data/theme/gnome-shell.css | 7 ++ js/js-resources.gresource.xml | 1 + js/ui/main.js | 3 + js/ui/osdMonitorLabeler.js | 141 ++++++++++++++++++++++++++++++++++ js/ui/shellDBus.js | 15 ++++ 5 files changed, 167 insertions(+) create mode 100644 js/ui/osdMonitorLabeler.js 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,