Add realm prefix to .desktop files imported into citadel

Change the way citadel-desktop-sync works so that gnome-shell
can be aware of applications from several realms at once.

Each application id is now placed in a separate namespace by
adding a prefix to the desktop filename.
This commit is contained in:
Bruce Leidl 2021-10-04 06:15:29 -04:00
parent e07d35944a
commit fa3f63b5c7
7 changed files with 89 additions and 24 deletions

View File

@ -3,7 +3,7 @@ use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use libcitadel::Result;
use libcitadel::{Result, Realm};
use std::io;
@ -19,8 +19,13 @@ pub struct DesktopFile {
impl DesktopFile {
pub fn write_to_dir<P: AsRef<Path>>(&self, directory: P) -> Result<()> {
let path = directory.as_ref().join(&self.filename);
pub fn write_to_dir<P: AsRef<Path>>(&self, directory: P, realm: Option<&Realm>) -> Result<()> {
let path = if let Some(r) = realm {
directory.as_ref().join(format!("realm-{}.{}", r.name(), self.filename))
} else {
directory.as_ref().join(&self.filename)
};
let f = File::create(&path)
.map_err(context!("failed to open desktop file {:?}", path))?;
self.write_to(f)

View File

@ -1,5 +1,5 @@
use std::collections::HashSet;
use std::ffi::{OsStr,OsString};
use std::ffi::OsStr;
use std::path::{Path,PathBuf};
use std::time::SystemTime;
@ -44,6 +44,15 @@ impl DesktopItem {
impl DesktopFileSync {
pub const CITADEL_APPLICATIONS: &'static str = "/home/citadel/.local/share/applications";
pub fn sync_active_realms() -> Result<()> {
let realms = Realms::load()?;
for realm in realms.active(true) {
let mut sync = DesktopFileSync::new(realm);
sync.run_sync(false)?;
}
Ok(())
}
pub fn new_current() -> Option<Self> {
Realms::load_current_realm()
.filter(|r| r.is_active())
@ -51,7 +60,7 @@ impl DesktopFileSync {
}
pub fn new(realm: Realm) -> Self {
let icons = match IconSync::new() {
let icons = match IconSync::new(&realm) {
Ok(icons) => Some(icons),
Err(e) => {
warn!("Error creating IconSync: {}", e);
@ -111,12 +120,15 @@ impl DesktopFileSync {
fn remove_missing_target_files(&mut self) -> Result<()> {
let sources = self.source_filenames();
let prefix = format!("realm-{}.", self.realm.name());
util::read_directory(Self::CITADEL_APPLICATIONS, |dent| {
if !sources.contains(&dent.file_name()) {
if let Some(filename) = dent.file_name().to_str() {
if filename.starts_with(&prefix) && !sources.contains(filename) {
let path = dent.path();
verbose!("Removing desktop entry that no longer exists: {:?}", path);
util::remove_file(path)?;
}
}
Ok(())
})
}
@ -125,10 +137,15 @@ impl DesktopFileSync {
path.metadata().and_then(|meta| meta.modified()).ok()
}
fn source_filenames(&self) -> HashSet<OsString> {
fn item_realm_filename(&self, item: &DesktopItem) -> Option<String> {
item.path.file_name().map(|s| {
format!("realm-{}.{}", self.realm.name(), s.to_string_lossy())
})
}
fn source_filenames(&self) -> HashSet<String> {
self.items.iter()
.flat_map(|item| item.path.file_name())
.map(|s| s.to_os_string())
.flat_map(|item| self.item_realm_filename(item))
.collect()
}
@ -137,7 +154,7 @@ impl DesktopFileSync {
let target = Path::new(Self::CITADEL_APPLICATIONS).join(item.filename());
if item.is_newer_than(&target) {
if let Err(e) = self.sync_item(item) {
warn!("Error synchronzing desktop file {:?} from realm-{}: {}", item.filename(), self.realm.name(), e);
warn!("Error synchronizing desktop file {:?} from realm-{}: {}", item.filename(), self.realm.name(), e);
}
}
}
@ -147,7 +164,7 @@ impl DesktopFileSync {
fn sync_item(&self, item: &DesktopItem) -> Result<()> {
let dfp = DesktopFileParser::parse_from_path(&item.path, "/usr/libexec/citadel-run ")?;
if dfp.is_showable() {
dfp.write_to_dir(Self::CITADEL_APPLICATIONS)?;
dfp.write_to_dir(Self::CITADEL_APPLICATIONS, Some(&self.realm))?;
if let Some(icon_name)= dfp.icon() {
if let Some(ref icons) = self.icons {
icons.sync_icon(icon_name)?;

View File

@ -1,11 +1,12 @@
use crate::sync::icon_cache::IconCache;
use std::collections::HashSet;
use std::path::Path;
use std::path::{Path, PathBuf};
use libcitadel::{Result, Realms, util};
use libcitadel::{Result, util, Realm};
use std::cell::{RefCell, Cell};
pub struct IconSync {
realm_base: PathBuf,
cache: IconCache,
known: RefCell<HashSet<String>>,
known_changed: Cell<bool>,
@ -16,12 +17,13 @@ impl IconSync {
const KNOWN_ICONS_FILE: &'static str = "/home/citadel/.local/share/icons/known.cache";
const PAPER_ICON_CACHE: &'static str = "/usr/share/icons/Paper/icon-theme.cache";
pub fn new() -> Result<Self> {
pub fn new(realm: &Realm) -> Result<Self> {
let realm_base= realm.base_path();
let cache = IconCache::open(Self::PAPER_ICON_CACHE)?;
let known = Self::read_known_cache()?;
let known = RefCell::new(known);
let known_changed = Cell::new(false);
Ok(IconSync { cache, known, known_changed })
Ok(IconSync { realm_base, cache, known, known_changed })
}
pub fn sync_icon(&self, icon_name: &str) -> Result<()> {
@ -56,6 +58,7 @@ impl IconSync {
let mut names: Vec<String> = self.known.borrow().iter().map(|s| s.to_string()).collect();
names.sort_unstable();
let out = names.join("\n") + "\n";
util::create_dir(Self::CITADEL_ICONS)?;
util::write_file(Self::KNOWN_ICONS_FILE, out)?;
Ok(())
}
@ -71,7 +74,7 @@ impl IconSync {
}
fn search(&self, subdir: impl AsRef<Path>, icon_name: &str) -> Result<bool> {
let base = Realms::current_realm_symlink().join(subdir.as_ref());
let base = self.realm_base.join(subdir.as_ref());
if !base.exists() {
return Ok(false)
}

View File

@ -8,14 +8,24 @@ mod icon_cache;
use self::desktop_sync::DesktopFileSync;
fn has_first_arg(args: &[String], arg: &str) -> bool {
args.len() > 1 && args[1].as_str() == arg
}
pub fn main(args: Vec<String>) {
Logger::set_log_level(LogLevel::Debug);
let clear = args.len() > 1 && args[1].as_str() == "--clear";
if has_first_arg(&args, "--all") {
if let Err(e) = DesktopFileSync::sync_active_realms() {
println!("Sync all active realms failed: {}", e);
}
} else {
let clear = has_first_arg(&args, "--clear");
if let Err(e) = sync(clear) {
println!("Desktop file sync failed: {}", e);
}
}
}
fn sync(clear: bool) -> Result<()> {

View File

@ -0,0 +1,30 @@
### Old Sync
#### citadel-current-watcher.path
[Path]
PathChanged=/run/citadel/realms/current
#### citadel-current-watcher.service
[Service]
Type=oneshot
ExecStart=/usr/libexec/citadel-desktop-sync --clear
ExecStart=/usr/bin/systemctl restart citadel-desktop-watcher.path
#### citadel-desktop-watcher.path
[Path]
PathChanged=/run/citadel/realms/current/current.realm/rootfs/usr/share/applications
PathChanged=/run/citadel/realms/current/current.realm/home/.local/share/applications
#### citadel-desktop-watcher.service
[Service]
Type=oneshot
ExecStart=/usr/libexec/citadel-desktop-sync
### New Sync
* Added a new command line option `--all` for syncronizing all active realms

View File

@ -4,5 +4,5 @@ StartLimitIntervalSec=0
[Service]
Type=oneshot
ExecStart=/usr/libexec/citadel-desktop-sync --clear
ExecStart=/usr/libexec/citadel-desktop-sync --all
ExecStart=/usr/bin/systemctl restart citadel-desktop-watcher.path

View File

@ -4,4 +4,4 @@ StartLimitIntervalSec=0
[Service]
Type=oneshot
ExecStart=/usr/libexec/citadel-desktop-sync
ExecStart=/usr/libexec/citadel-desktop-sync --all