Claude/gnome wallpaper auto rotation r1ailq #1
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Loads the GObject-Introspection bindings the live wallpaper feature needs
|
||||||
|
* (Gst, GstApp for the appsink signals, Cogl for uploading decoded frames)
|
||||||
|
* and initializes GStreamer. Shared by extension.js (to actually run the
|
||||||
|
* pipeline) and prefs.js (to tell the user up front why the feature is
|
||||||
|
* greyed out), since either process may be missing the system packages
|
||||||
|
* that provide these typelibs.
|
||||||
|
*
|
||||||
|
* Throws with a descriptive message on failure; never caches a *failure*,
|
||||||
|
* since the user may install the missing packages and reopen preferences
|
||||||
|
* without restarting the shell.
|
||||||
|
*/
|
||||||
|
export async function loadGstreamerModules() {
|
||||||
|
const [{default: Gst}, , {default: Cogl}] = await Promise.all([
|
||||||
|
import('gi://Gst?version=1.0'),
|
||||||
|
import('gi://GstApp?version=1.0'),
|
||||||
|
import('gi://Cogl'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!Gst.is_initialized())
|
||||||
|
Gst.init(null);
|
||||||
|
|
||||||
|
return {Gst, Cogl};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GSTREAMER_INSTALL_HINT =
|
||||||
|
'Install GStreamer’s "base" and "good" plugin sets (with their GObject-Introspection data), ' +
|
||||||
|
'which provide playback and GIF decoding:\n' +
|
||||||
|
'• Arch: sudo pacman -S gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav\n' +
|
||||||
|
'• Debian/Ubuntu: sudo apt install gstreamer1.0-plugins-base gstreamer1.0-plugins-good ' +
|
||||||
|
'gir1.2-gst-plugins-base-1.0\n' +
|
||||||
|
'• Fedora: sudo dnf install gstreamer1-plugins-base gstreamer1-plugins-good gobject-introspection\n' +
|
||||||
|
'Then restart GNOME Shell (log out and back in on Wayland).';
|
||||||
+3
-16
@@ -4,6 +4,7 @@ import Clutter from 'gi://Clutter';
|
|||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
|
|
||||||
import {SettingsKey} from './settingsKeys.js';
|
import {SettingsKey} from './settingsKeys.js';
|
||||||
|
import {loadGstreamerModules, GSTREAMER_INSTALL_HINT} from './gstreamerAvailability.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays a video or animated GIF file as a looping animated background.
|
* Plays a video or animated GIF file as a looping animated background.
|
||||||
@@ -88,28 +89,14 @@ export class LiveWallpaperManager {
|
|||||||
|
|
||||||
async _loadGstreamer() {
|
async _loadGstreamer() {
|
||||||
try {
|
try {
|
||||||
// GstApp's binding itself is unused, but importing it registers
|
const {Gst, Cogl} = await loadGstreamerModules();
|
||||||
// GstApp.AppSink with GJS so appsink elements expose 'new-sample'
|
|
||||||
// and the 'pull-sample' action signal instead of being wrapped
|
|
||||||
// as a generic, capability-less Gst.Element.
|
|
||||||
const [{default: Gst}, , {default: Cogl}] = await Promise.all([
|
|
||||||
import('gi://Gst?version=1.0'),
|
|
||||||
import('gi://GstApp?version=1.0'),
|
|
||||||
import('gi://Cogl'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this._Gst = Gst;
|
this._Gst = Gst;
|
||||||
this._Cogl = Cogl;
|
this._Cogl = Cogl;
|
||||||
|
|
||||||
if (!Gst.is_initialized())
|
|
||||||
Gst.init(null);
|
|
||||||
|
|
||||||
this._available = true;
|
this._available = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._available = false;
|
this._available = false;
|
||||||
this._logger.warn(
|
this._logger.warn(
|
||||||
'Live wallpaper unavailable: GStreamer/Cogl introspection bindings could not be loaded ' +
|
`Live wallpaper unavailable: GStreamer/Cogl introspection bindings could not be loaded (${e.message ?? e}).\n${GSTREAMER_INSTALL_HINT}`);
|
||||||
`(${e.message ?? e}). Install GStreamer with its "good"/"base" plugin sets to enable this feature.`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,17 @@ import Gdk from 'gi://Gdk';
|
|||||||
import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
||||||
|
|
||||||
import {SettingsKey} from './lib/settingsKeys.js';
|
import {SettingsKey} from './lib/settingsKeys.js';
|
||||||
|
import {loadGstreamerModules, GSTREAMER_INSTALL_HINT} from './lib/gstreamerAvailability.js';
|
||||||
|
|
||||||
export default class BenthicBloomPreferences extends ExtensionPreferences {
|
export default class BenthicBloomPreferences extends ExtensionPreferences {
|
||||||
fillPreferencesWindow(window) {
|
async fillPreferencesWindow(window) {
|
||||||
const settings = this.getSettings();
|
const settings = this.getSettings();
|
||||||
|
const gstreamerError = await loadGstreamerModules().then(() => null, e => e.message ?? String(e));
|
||||||
|
|
||||||
window.set_default_size(640, 720);
|
window.set_default_size(640, 720);
|
||||||
window.add(this._buildGeneralPage(settings));
|
window.add(this._buildGeneralPage(settings));
|
||||||
window.add(this._buildRotationPage(settings));
|
window.add(this._buildRotationPage(settings));
|
||||||
window.add(this._buildLiveWallpaperPage(settings));
|
window.add(this._buildLiveWallpaperPage(settings, gstreamerError));
|
||||||
window.add(this._buildOledPage(settings));
|
window.add(this._buildOledPage(settings));
|
||||||
window.add(this._buildAboutPage());
|
window.add(this._buildAboutPage());
|
||||||
}
|
}
|
||||||
@@ -170,9 +172,21 @@ export default class BenthicBloomPreferences extends ExtensionPreferences {
|
|||||||
|
|
||||||
// --- Live wallpaper page --------------------------------------------
|
// --- Live wallpaper page --------------------------------------------
|
||||||
|
|
||||||
_buildLiveWallpaperPage(settings) {
|
_buildLiveWallpaperPage(settings, gstreamerError) {
|
||||||
const page = new Adw.PreferencesPage({title: _('Live Wallpaper'), icon_name: 'video-x-generic-symbolic'});
|
const page = new Adw.PreferencesPage({title: _('Live Wallpaper'), icon_name: 'video-x-generic-symbolic'});
|
||||||
|
|
||||||
|
if (gstreamerError) {
|
||||||
|
const warningGroup = new Adw.PreferencesGroup();
|
||||||
|
const warningRow = new Adw.ActionRow({
|
||||||
|
title: _('GStreamer Not Found'),
|
||||||
|
subtitle: `${_('Live wallpapers will stay disabled until this is fixed:')} ${gstreamerError}\n\n${GSTREAMER_INSTALL_HINT}`,
|
||||||
|
css_classes: ['warning'],
|
||||||
|
});
|
||||||
|
warningRow.subtitle_lines = 0;
|
||||||
|
warningGroup.add(warningRow);
|
||||||
|
page.add(warningGroup);
|
||||||
|
}
|
||||||
|
|
||||||
const group = new Adw.PreferencesGroup({
|
const group = new Adw.PreferencesGroup({
|
||||||
title: _('Video Wallpaper'),
|
title: _('Video Wallpaper'),
|
||||||
description: _(
|
description: _(
|
||||||
@@ -221,17 +235,19 @@ export default class BenthicBloomPreferences extends ExtensionPreferences {
|
|||||||
fileRow.add_suffix(chooseButton);
|
fileRow.add_suffix(chooseButton);
|
||||||
group.add(fileRow);
|
group.add(fileRow);
|
||||||
|
|
||||||
group.add(this._switchRow(
|
const muteRow = this._switchRow(
|
||||||
settings, SettingsKey.LIVE_WALLPAPER_MUTED,
|
settings, SettingsKey.LIVE_WALLPAPER_MUTED,
|
||||||
_('Mute Audio'), _('Play video wallpapers without sound')));
|
_('Mute Audio'), _('Play video wallpapers without sound'));
|
||||||
group.add(this._spinRow(
|
group.add(muteRow);
|
||||||
|
const rateRow = this._spinRow(
|
||||||
{
|
{
|
||||||
title: _('Playback Speed'),
|
title: _('Playback Speed'),
|
||||||
subtitle: _('Multiplier, e.g. 0.5 for half speed, 2.0 for double speed'),
|
subtitle: _('Multiplier, e.g. 0.5 for half speed, 2.0 for double speed'),
|
||||||
lower: 0.1, upper: 4.0, step: 0.1, page: 0.5, digits: 1,
|
lower: 0.1, upper: 4.0, step: 0.1, page: 0.5, digits: 1,
|
||||||
},
|
},
|
||||||
() => settings.get_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE),
|
() => settings.get_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE),
|
||||||
value => settings.set_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE, value)));
|
value => settings.set_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE, value));
|
||||||
|
group.add(rateRow);
|
||||||
|
|
||||||
const powerGroup = new Adw.PreferencesGroup({title: _('Power Saving')});
|
const powerGroup = new Adw.PreferencesGroup({title: _('Power Saving')});
|
||||||
page.add(powerGroup);
|
page.add(powerGroup);
|
||||||
@@ -242,6 +258,16 @@ export default class BenthicBloomPreferences extends ExtensionPreferences {
|
|||||||
settings, SettingsKey.LIVE_WALLPAPER_PAUSE_WHEN_FULLSCREEN,
|
settings, SettingsKey.LIVE_WALLPAPER_PAUSE_WHEN_FULLSCREEN,
|
||||||
_('Pause When Fullscreen'), _('Stop video playback while a window is fullscreen')));
|
_('Pause When Fullscreen'), _('Stop video playback while a window is fullscreen')));
|
||||||
|
|
||||||
|
// The enable switch itself stays usable so the setting can be
|
||||||
|
// prepared ahead of time, but everything that only matters once
|
||||||
|
// GStreamer is actually driving playback is greyed out.
|
||||||
|
if (gstreamerError) {
|
||||||
|
fileRow.sensitive = false;
|
||||||
|
muteRow.sensitive = false;
|
||||||
|
rateRow.sensitive = false;
|
||||||
|
powerGroup.sensitive = false;
|
||||||
|
}
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user