Skip to content

Instantly share code, notes, and snippets.

@marcinjahn
Last active May 5, 2023 17:45
Show Gist options
  • Save marcinjahn/7acadd90e3d8299ffd2f67470f18b10b to your computer and use it in GitHub Desktop.
Save marcinjahn/7acadd90e3d8299ffd2f67470f18b10b to your computer and use it in GitHub Desktop.
prefs.js with failure "JS ERROR: Failed to open preferences: ImportError: No JS module 'ui' found in search path"
const GIRepository = imports.gi.GIRepository;
const GLib = imports.gi.GLib;
let libdir = GIRepository.Repository.get_search_path().find(path => {
return path.endsWith("/gjs/girepository-1.0");
}).replace("/gjs/girepository-1.0", "");
const gsdir = GLib.build_filenamev([libdir, "gnome-shell"]);
if (!GLib.file_test(gsdir, GLib.FileTest.IS_DIR)) {
const currentDir = `/${GLib.path_get_basename(libdir)}`;
libdir = libdir.replace(currentDir, "");
}
const typelibDir = GLib.build_filenamev([libdir, "gnome-shell"]);
GIRepository.Repository.prepend_search_path(typelibDir);
GIRepository.Repository.prepend_library_path(typelibDir);
var prefs = (function (gvc1, adw1, gtk4) {
'use strict';
function delay(miliseconds) {
return new Promise(resolve => {
setTimeout(() => resolve(undefined), miliseconds);
});
}
const VolumeStatus = imports.ui.status.volume;
const Main = imports.ui.main;
const QuickSettings = Main.panel.statusArea.quickSettings;
async function getVisibleDevices() {
const mixer = VolumeStatus.getMixerControl();
await waitForReady(mixer);
const devices = QuickSettings._volume._output._deviceItems;
// value is PopupImageMenuItem, label is St.Label
return Array.from(devices, ([id, value]) => ({
id,
displayName: value.label.get_text()
}));
}
async function getOutputDevicesInfo(ids) {
const mixer = VolumeStatus.getMixerControl();
await waitForReady(mixer);
return ids.map(id => ({
id,
displayName: mixer.lookup_output_id(id).description
}));
}
async function waitForReady(mixer) {
while (mixer.get_state() === gvc1.MixerControlState.CONNECTING) {
await delay(200);
}
const state = mixer.get_state();
if (state === gvc1.MixerControlState.FAILED) {
throw new Error('MixerControl is in a failed state');
}
else if (state === gvc1.MixerControlState.CLOSED) {
throw new Error('MixerControl is in a closed state');
}
}
const ExtensionUtils = imports.misc.extensionUtils;
const SettingsPath = 'com.marcinjahn.exampleextension';
const excludedOutputsSetting = 'excluded-outputs';
function getExcludedOutputDeviceIds() {
const settings = ExtensionUtils.getSettings(SettingsPath);
const ids = settings.get_strv(excludedOutputsSetting);
return ids.map(id => parseInt(id));
}
imports.misc.extensionUtils;
function init() {
}
async function fillPreferencesWindow(window) {
const visibleDevicesPromise = getVisibleDevices();
const hiddenDevicesPromise = getOutputDevicesInfo(getExcludedOutputDeviceIds());
const page = new adw1.PreferencesPage();
const group = new adw1.PreferencesGroup();
page.add(group);
const [visibleDevices, hiddenDevices] = await Promise.all([
visibleDevicesPromise,
hiddenDevicesPromise
]);
visibleDevices.forEach(device => {
group.add(createDeviceRow(device, true));
});
hiddenDevices.forEach(device => {
group.add(createDeviceRow(device, false));
});
window.add(page);
}
function createDeviceRow(device, active) {
const row = new adw1.ActionRow({ title: device.displayName });
const toggle = new gtk4.Switch({
active,
valign: gtk4.Align.CENTER,
});
// Add the switch to the row
row.add_suffix(toggle);
row.activatable_widget = toggle;
return row;
}
var prefs = { init, fillPreferencesWindow };
return prefs;
})(imports.gi.Gvc, imports.gi.Adw, imports.gi.Gtk);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment