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

View File

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

View File

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

View File

@ -8,13 +8,23 @@ mod icon_cache;
use self::desktop_sync::DesktopFileSync; 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>) { pub fn main(args: Vec<String>) {
Logger::set_log_level(LogLevel::Debug); Logger::set_log_level(LogLevel::Debug);
let clear = args.len() > 1 && args[1].as_str() == "--clear";
if let Err(e) = sync(clear) { if has_first_arg(&args, "--all") {
println!("Desktop file sync failed: {}", e); 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);
}
} }
} }

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] [Service]
Type=oneshot 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 ExecStart=/usr/bin/systemctl restart citadel-desktop-watcher.path

View File

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