Support setting heads-up-display message from file
Resolves: https://issues.redhat.com/browse/RHEL-4096
This commit is contained in:
parent
f5f7592e03
commit
e31d3f3f9a
@ -38,6 +38,8 @@ Patch: 0001-Include-status-icons-in-classic-session.patch
|
||||
|
||||
Patch: window-list-reordering.patch
|
||||
|
||||
Patch: heads-up-display-from-file.patch
|
||||
|
||||
%description
|
||||
GNOME Shell Extensions is a collection of extensions providing additional and
|
||||
optional functionality to GNOME Shell.
|
||||
|
||||
483
heads-up-display-from-file.patch
Normal file
483
heads-up-display-from-file.patch
Normal file
@ -0,0 +1,483 @@
|
||||
From c9845372267ecc085143b3cfa4f7eaf39091b41a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 May 2025 13:29:06 +0200
|
||||
Subject: [PATCH 1/5] heads-up-display: Split out getMessageText() helper
|
||||
|
||||
The new method will make it easier to add alternative sources
|
||||
for the message text.
|
||||
---
|
||||
extensions/heads-up-display/extension.js | 10 ++++++++--
|
||||
1 file changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js
|
||||
index a71b5925..5f72e168 100644
|
||||
--- a/extensions/heads-up-display/extension.js
|
||||
+++ b/extensions/heads-up-display/extension.js
|
||||
@@ -183,6 +183,13 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
this._onUserIdle.bind(this));
|
||||
}
|
||||
|
||||
+ _getMessageText() {
|
||||
+ return {
|
||||
+ heading: this._settings.get_string('message-heading'),
|
||||
+ body: this._settings.get_string('message-body'),
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
_updateMessage() {
|
||||
if (this._messageInhibitedUntilIdle) {
|
||||
if (this._message)
|
||||
@@ -237,8 +244,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
- const heading = this._settings.get_string('message-heading');
|
||||
- const body = this._settings.get_string('message-body');
|
||||
+ const {heading, body} = this._getMessageText();
|
||||
|
||||
if (!heading && !body) {
|
||||
this._dismissMessage();
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From a121cf4e1a21e78b74de7b2f33f40f3201eaaa50 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 May 2025 13:29:42 +0200
|
||||
Subject: [PATCH 2/5] heads-up-display: Update message text asynchronously
|
||||
|
||||
We will soon allow reading the banner text from a file. Prepare
|
||||
for that by making the method asynchronous.
|
||||
---
|
||||
extensions/heads-up-display/extension.js | 27 +++++++++++++-----------
|
||||
1 file changed, 15 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js
|
||||
index 5f72e168..3ae6eabb 100644
|
||||
--- a/extensions/heads-up-display/extension.js
|
||||
+++ b/extensions/heads-up-display/extension.js
|
||||
@@ -66,7 +66,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
enable() {
|
||||
this._settings = this.getSettings('org.gnome.shell.extensions.heads-up-display');
|
||||
this._settings.connectObject('changed',
|
||||
- () => this._updateMessage(), this);
|
||||
+ () => this._updateMessage().catch(logError), this);
|
||||
|
||||
this._idleMonitor = global.backend.get_core_idle_monitor();
|
||||
this._messageInhibitedUntilIdle = false;
|
||||
@@ -123,15 +123,15 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
|
||||
_onStartupComplete() {
|
||||
Main.overview.connectObject(
|
||||
- 'showing', () => this._updateMessage(),
|
||||
- 'hidden', () => this._updateMessage(),
|
||||
+ 'showing', () => this._updateMessage().catch(logError),
|
||||
+ 'hidden', () => this._updateMessage().catch(logError),
|
||||
this);
|
||||
Main.layoutManager.panelBox.connectObject('notify::visible',
|
||||
- () => this._updateMessage(), this);
|
||||
+ () => this._updateMessage().catch(logError), this);
|
||||
Main.sessionMode.connectObject('updated',
|
||||
() => this._onSessionModeUpdated(), this);
|
||||
|
||||
- this._updateMessage();
|
||||
+ this._updateMessage().catch(logError);
|
||||
}
|
||||
|
||||
_onSessionModeUpdated() {
|
||||
@@ -140,13 +140,14 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
|
||||
const dialog = Main.screenShield._dialog;
|
||||
if (!Main.sessionMode.isGreeter && dialog && !this._screenShieldVisibleId) {
|
||||
- this._screenShieldVisibleId = dialog._clock.connect('notify::visible', this._updateMessage.bind(this));
|
||||
+ this._screenShieldVisibleId = dialog._clock.connect('notify::visible',
|
||||
+ () => this._updateMessage().catch(logError));
|
||||
this._screenShieldDestroyId = dialog._clock.connect('destroy', () => {
|
||||
this._screenShieldVisibleId = 0;
|
||||
this._screenShieldDestroyId = 0;
|
||||
});
|
||||
}
|
||||
- this._updateMessage();
|
||||
+ this._updateMessage().catch(logError);
|
||||
}
|
||||
|
||||
_stopWatchingForIdle() {
|
||||
@@ -168,7 +169,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
|
||||
_onUserIdle() {
|
||||
this._messageInhibitedUntilIdle = false;
|
||||
- this._updateMessage();
|
||||
+ this._updateMessage().catch(logError);
|
||||
}
|
||||
|
||||
_watchForIdle() {
|
||||
@@ -183,14 +184,16 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
this._onUserIdle.bind(this));
|
||||
}
|
||||
|
||||
- _getMessageText() {
|
||||
+ async _getMessageText() {
|
||||
+ await false;
|
||||
+
|
||||
return {
|
||||
heading: this._settings.get_string('message-heading'),
|
||||
body: this._settings.get_string('message-body'),
|
||||
};
|
||||
}
|
||||
|
||||
- _updateMessage() {
|
||||
+ async _updateMessage() {
|
||||
if (this._messageInhibitedUntilIdle) {
|
||||
if (this._message)
|
||||
this._dismissMessage();
|
||||
@@ -244,7 +247,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
- const {heading, body} = this._getMessageText();
|
||||
+ const {heading, body} = await this._getMessageText();
|
||||
|
||||
if (!heading && !body) {
|
||||
this._dismissMessage();
|
||||
@@ -276,7 +279,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
|
||||
this._watchForIdle();
|
||||
this._messageInhibitedUntilIdle = true;
|
||||
- this._updateMessage();
|
||||
+ this._updateMessage().catch(logError);
|
||||
}
|
||||
|
||||
_dismissMessage() {
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 88bff8c0a592e72f5797c834dfa8519b69c1f15b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 May 2025 14:49:15 +0200
|
||||
Subject: [PATCH 3/5] heads-up-display: Add `message-path` and `-source`
|
||||
settings
|
||||
|
||||
The new settings allow reading the message text from a file instead
|
||||
of GSettings. This is mainly useful for `/etc/motd` and similar
|
||||
mechanisms, to show the same message for both graphical and
|
||||
non-graphical logins.
|
||||
---
|
||||
...ll.extensions.heads-up-display.gschema.xml | 20 +++++++++++++++++++
|
||||
1 file changed, 20 insertions(+)
|
||||
|
||||
diff --git a/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
index 1e2119c8..a9552642 100644
|
||||
--- a/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
+++ b/extensions/heads-up-display/org.gnome.shell.extensions.heads-up-display.gschema.xml
|
||||
@@ -5,6 +5,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
<schemalist gettext-domain="gnome-shell-extensions">
|
||||
+ <enum id="org.gnome.shell.extensions.heads-up-display.MessageSource">
|
||||
+ <value value="1" nick="settings"/>
|
||||
+ <value value="2" nick="file"/>
|
||||
+ </enum>
|
||||
<schema id="org.gnome.shell.extensions.heads-up-display"
|
||||
path="/org/gnome/shell/extensions/heads-up-display/">
|
||||
<key name="idle-timeout" type="u">
|
||||
@@ -14,6 +18,15 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
Number of seconds until message is reshown after user goes idle.
|
||||
</description>
|
||||
</key>
|
||||
+ <key name="message-source" enum="org.gnome.shell.extensions.heads-up-display.MessageSource">
|
||||
+ <default>"settings"</default>
|
||||
+ <summary>
|
||||
+ Banner message source
|
||||
+ </summary>
|
||||
+ <description>
|
||||
+ The source of the text banner message on the login screen.
|
||||
+ </description>
|
||||
+ </key>
|
||||
<key name="message-heading" type="s">
|
||||
<default>""</default>
|
||||
<summary>Message to show at top of display</summary>
|
||||
@@ -28,6 +41,13 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
A message to always show at the top of the screen.
|
||||
</description>
|
||||
</key>
|
||||
+ <key name="message-path" type="s">
|
||||
+ <default>""</default>
|
||||
+ <summary>Banner message path</summary>
|
||||
+ <description>
|
||||
+ Path to text file with message to show in banner
|
||||
+ </description>
|
||||
+ </key>
|
||||
<key name="show-on-login-screen" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show on login screen</summary>
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 706aa6d5ee89bfb196964b5f1ffd1373ba66d4c4 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 May 2025 14:49:15 +0200
|
||||
Subject: [PATCH 4/5] heads-up-display: Support loading message text from file
|
||||
|
||||
Support the new `message-path` and `message-source` settings, which
|
||||
allows loading the message text from a path instead of GSettings.
|
||||
|
||||
This is mainly useful for `/etc/motd` and similar mechanisms, to
|
||||
show the same message for both graphical and non-graphical logins.
|
||||
---
|
||||
extensions/heads-up-display/extension.js | 70 +++++++++++++++++++++++-
|
||||
1 file changed, 69 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/extensions/heads-up-display/extension.js b/extensions/heads-up-display/extension.js
|
||||
index 3ae6eabb..5a377415 100644
|
||||
--- a/extensions/heads-up-display/extension.js
|
||||
+++ b/extensions/heads-up-display/extension.js
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
+import Gio from 'gi://Gio';
|
||||
import GObject from 'gi://GObject';
|
||||
import Meta from 'gi://Meta';
|
||||
import Mtk from 'gi://Mtk';
|
||||
@@ -13,6 +14,8 @@ import {MonitorConstraint} from 'resource:///org/gnome/shell/ui/layout.js';
|
||||
|
||||
import {HeadsUpMessage} from './headsUpMessage.js';
|
||||
|
||||
+Gio._promisify(Gio.File.prototype, 'load_contents_async');
|
||||
+
|
||||
var HeadsUpConstraint = GObject.registerClass({
|
||||
Properties: {
|
||||
'offset': GObject.ParamSpec.int(
|
||||
@@ -67,6 +70,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
this._settings = this.getSettings('org.gnome.shell.extensions.heads-up-display');
|
||||
this._settings.connectObject('changed',
|
||||
() => this._updateMessage().catch(logError), this);
|
||||
+ this._cachedMessage = {};
|
||||
|
||||
this._idleMonitor = global.backend.get_core_idle_monitor();
|
||||
this._messageInhibitedUntilIdle = false;
|
||||
@@ -81,6 +85,7 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
|
||||
disable() {
|
||||
this._dismissMessage();
|
||||
+ this._clearMessageFile();
|
||||
|
||||
this._stopWatchingForIdle();
|
||||
|
||||
@@ -184,8 +189,71 @@ export default class HeadsUpDisplayExtension extends Extension {
|
||||
this._onUserIdle.bind(this));
|
||||
}
|
||||
|
||||
+ _clearMessageFile() {
|
||||
+ this._messageFileMonitor?.disconnectObject(this);
|
||||
+ this._messageFileMonitor = null;
|
||||
+
|
||||
+ this._messageFile = null;
|
||||
+ }
|
||||
+
|
||||
+ _updateMessageFile() {
|
||||
+ const path = this._settings.get_string('message-source') === 'file'
|
||||
+ ? this._settings.get_string('message-path')
|
||||
+ : null;
|
||||
+ const file = path
|
||||
+ ? Gio.File.new_for_path(path)
|
||||
+ : null;
|
||||
+
|
||||
+ if (!file && !this._messageFile)
|
||||
+ return false;
|
||||
+
|
||||
+ if (file && this._messageFile && this._messageFile.equal(file))
|
||||
+ return false;
|
||||
+
|
||||
+ this._clearMessageFile();
|
||||
+ this._messageFile = file;
|
||||
+
|
||||
+ if (file) {
|
||||
+ this._messageFileMonitor = file.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
+ this._messageFileMonitor.connectObject(
|
||||
+ 'changed', () => this._messageFileChanged().catch(logError), this);
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ async _messageFileChanged() {
|
||||
+ await this._updateCachedMessage();
|
||||
+ await this._updateMessage();
|
||||
+ }
|
||||
+
|
||||
+ async _updateCachedMessage() {
|
||||
+ let heading = null, body = null;
|
||||
+
|
||||
+ if (this._messageFile) {
|
||||
+ try {
|
||||
+ const [contents] = await this._messageFile.load_contents_async(null);
|
||||
+ const message = new TextDecoder().decode(contents);
|
||||
+
|
||||
+ const sep = '\n\n';
|
||||
+ const paragraphs = message.split(sep);
|
||||
+ if (paragraphs.length > 1)
|
||||
+ heading = paragraphs.shift();
|
||||
+ body = paragraphs.join(sep);
|
||||
+ } catch (e) {
|
||||
+ console.error(`Failed to read banner from ${this._messageFile.get_path()}: ${e.message}`);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ this._cachedMessage = {heading, body};
|
||||
+ }
|
||||
+
|
||||
async _getMessageText() {
|
||||
- await false;
|
||||
+ if (this._updateMessageFile())
|
||||
+ await this._updateCachedMessage();
|
||||
+
|
||||
+ if (this._settings.get_string('message-source') === 'file')
|
||||
+ return this._cachedMessage;
|
||||
|
||||
return {
|
||||
heading: this._settings.get_string('message-heading'),
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 1468fedb6e15225741aae830b30555f63a228ae5 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
|
||||
Date: Fri, 9 May 2025 13:27:35 +0200
|
||||
Subject: [PATCH 5/5] heads-up-display: Expose `message-path` and `-source`
|
||||
settings in prefs
|
||||
|
||||
Allow switching between directly entered message text and selecting
|
||||
a file from the extension's prefs dialog.
|
||||
---
|
||||
extensions/heads-up-display/prefs.js | 88 +++++++++++++++++++++++++++-
|
||||
1 file changed, 85 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/extensions/heads-up-display/prefs.js b/extensions/heads-up-display/prefs.js
|
||||
index 304c8813..1d2bea4c 100644
|
||||
--- a/extensions/heads-up-display/prefs.js
|
||||
+++ b/extensions/heads-up-display/prefs.js
|
||||
@@ -4,11 +4,14 @@
|
||||
|
||||
import Adw from 'gi://Adw';
|
||||
import Gio from 'gi://Gio';
|
||||
+import GLib from 'gi://GLib';
|
||||
import GObject from 'gi://GObject';
|
||||
import Gtk from 'gi://Gtk';
|
||||
|
||||
import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||
|
||||
+Gio._promisify(Gtk.FileDialog.prototype, 'open');
|
||||
+
|
||||
class GeneralGroup extends Adw.PreferencesGroup {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
@@ -64,21 +67,100 @@ class MessageGroup extends Adw.PreferencesGroup {
|
||||
title: _('Message'),
|
||||
});
|
||||
|
||||
+ this._settings = settings;
|
||||
+
|
||||
+ const actionGroup = new Gio.SimpleActionGroup();
|
||||
+ const selectAction = new Gio.SimpleAction({name: 'select-file'});
|
||||
+ actionGroup.add_action(selectAction);
|
||||
+ actionGroup.add_action(settings.create_action('message-source'));
|
||||
+ this.insert_action_group('message-group', actionGroup);
|
||||
+
|
||||
+ selectAction.connect('activate',
|
||||
+ () => this._selectFile().catch(logError));
|
||||
+
|
||||
+ const textCheck = new Gtk.CheckButton({
|
||||
+ action_name: 'message-group.message-source',
|
||||
+ action_target: new GLib.Variant('s', 'settings'),
|
||||
+ });
|
||||
+ const useTextRow = new Adw.ActionRow({
|
||||
+ title: _('Use fixed message text'),
|
||||
+ activatable_widget: textCheck,
|
||||
+ });
|
||||
+ useTextRow.add_prefix(textCheck);
|
||||
+
|
||||
+ this.add(useTextRow);
|
||||
+
|
||||
const textView = new Gtk.TextView({
|
||||
accepts_tab: false,
|
||||
wrap_mode: Gtk.WrapMode.WORD,
|
||||
+ height_request: 80,
|
||||
top_margin: 6,
|
||||
bottom_margin: 6,
|
||||
left_margin: 6,
|
||||
right_margin: 6,
|
||||
- vexpand: true,
|
||||
});
|
||||
- textView.add_css_class('card');
|
||||
|
||||
settings.bind('message-body',
|
||||
textView.get_buffer(), 'text',
|
||||
Gio.SettingsBindFlags.DEFAULT);
|
||||
- this.add(textView);
|
||||
+
|
||||
+ const textRow = new Adw.PreferencesRow({
|
||||
+ child: textView,
|
||||
+ });
|
||||
+ this.add(textRow);
|
||||
+
|
||||
+ textCheck.bind_property('active',
|
||||
+ textRow, 'sensitive',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
+ const fileCheck = new Gtk.CheckButton({
|
||||
+ action_name: 'message-group.message-source',
|
||||
+ action_target: new GLib.Variant('s', 'file'),
|
||||
+ group: textCheck,
|
||||
+ });
|
||||
+ const useFileRow = new Adw.ActionRow({
|
||||
+ title: _('Read message text from file'),
|
||||
+ activatable_widget: fileCheck,
|
||||
+ });
|
||||
+ useFileRow.add_prefix(fileCheck);
|
||||
+
|
||||
+ this.add(useFileRow);
|
||||
+
|
||||
+ const selectButton = new Gtk.Button({
|
||||
+ label: _('Select…'),
|
||||
+ action_name: 'message-group.select-file',
|
||||
+ valign: Gtk.Align.CENTER,
|
||||
+ });
|
||||
+
|
||||
+ const fileRow = new Adw.ActionRow();
|
||||
+ fileRow.add_suffix(selectButton);
|
||||
+ this.add(fileRow);
|
||||
+
|
||||
+ fileCheck.bind_property('active',
|
||||
+ fileRow, 'sensitive',
|
||||
+ GObject.BindingFlags.SYNC_CREATE);
|
||||
+
|
||||
+ function updateFileLabel() {
|
||||
+ const path = settings.get_string('message-path');
|
||||
+ fileRow.title = path || _('None');
|
||||
+ }
|
||||
+
|
||||
+ settings.connect_object('changed::message-path',
|
||||
+ () => updateFileLabel(),
|
||||
+ this, GObject.ConnectFlags.DEFAULT);
|
||||
+ updateFileLabel();
|
||||
+ }
|
||||
+
|
||||
+ async _selectFile() {
|
||||
+ const fileDialog = new Gtk.FileDialog({
|
||||
+ modal: true,
|
||||
+ default_filter: new Gtk.FileFilter({
|
||||
+ mime_types: ['text/plain'],
|
||||
+ }),
|
||||
+ });
|
||||
+ const file = await fileDialog.open(this.get_root(), null);
|
||||
+ if (file)
|
||||
+ this._settings.set_string('message-path', file.get_path());
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.49.0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user