484 lines
17 KiB
Diff
484 lines
17 KiB
Diff
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.46.1
|
|
|
|
|
|
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.46.1
|
|
|
|
|
|
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.46.1
|
|
|
|
|
|
From a1f32f7ce76044c4782dc9e3620e4ba448cfec82 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..bc1961b9 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).trim();
|
|
+
|
|
+ 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.46.1
|
|
|
|
|
|
From 6bdf47e04f64b41db0350b073f264ca46ed6f9c0 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.46.1
|
|
|