mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-13 08:01:05 +01:00
Merge pull request #79 from pinnacle-comp/xdg
Xdg Base Directories support
This commit is contained in:
commit
aa3d69794e
5 changed files with 151 additions and 52 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -6,11 +6,9 @@ edition = "2021"
|
|||
license = "GPL-3.0+"
|
||||
description = "A WIP Smithay-based Wayland compositor, inspired by AwesomeWM"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Ottatop/pinnacle/"
|
||||
repository = "https://github.com/pinnacle-comp/pinnacle/"
|
||||
keywords = ["wayland", "compositor", "smithay", "lua"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
|
@ -25,13 +23,18 @@ rmp-serde = { version = "1.1.2" }
|
|||
calloop = { version = "0.10.1", features = ["executor", "futures-io"] }
|
||||
futures-lite = { version = "1.13.0" }
|
||||
async-process = { version = "1.7.0" }
|
||||
itertools = { version = "0.11.0" }
|
||||
x11rb = { version = "0.12.0", default-features = false, features = ["composite"], optional = true }
|
||||
shellexpand = "3.1.0"
|
||||
toml = "0.7.7"
|
||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||
clap = { version = "4.4.2", features = ["derive"] }
|
||||
xkbcommon = "0.6.0"
|
||||
xdg = "2.5.2"
|
||||
lazy_static = "1.4.0"
|
||||
tracing-appender = "0.2.2"
|
||||
walkdir = "2.4.0"
|
||||
sysinfo = "0.29.10"
|
||||
|
||||
|
||||
[features]
|
||||
default = ["egl", "winit", "udev", "xwayland"]
|
||||
|
|
51
src/api.rs
51
src/api.rs
|
@ -46,10 +46,13 @@ use anyhow::Context;
|
|||
use smithay::reexports::calloop::{
|
||||
self, channel::Sender, generic::Generic, EventSource, Interest, Mode, PostAction,
|
||||
};
|
||||
use sysinfo::{ProcessRefreshKind, RefreshKind, SystemExt};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use self::msg::{Msg, OutgoingMsg};
|
||||
|
||||
pub const DEFAULT_SOCKET_DIR: &str = "/tmp";
|
||||
pub const SOCKET_NAME: &str = "pinnacle_socket";
|
||||
|
||||
fn handle_client(
|
||||
mut stream: UnixStream,
|
||||
|
@ -90,11 +93,51 @@ impl PinnacleSocketSource {
|
|||
/// Create a loop source that listens for connections to the provided socket_dir.
|
||||
/// This will also set PINNACLE_SOCKET for use in API implementations.
|
||||
pub fn new(sender: Sender<Msg>, socket_dir: &Path) -> anyhow::Result<Self> {
|
||||
let socket_path = socket_dir.join("pinnacle_socket");
|
||||
let system = sysinfo::System::new_with_specifics(
|
||||
RefreshKind::new().with_processes(ProcessRefreshKind::new()),
|
||||
);
|
||||
|
||||
if let Ok(exists) = socket_path.try_exists() {
|
||||
if exists {
|
||||
std::fs::remove_file(&socket_path).context("Failed to remove old socket")?;
|
||||
// Test if you are running multiple instances of Pinnacle
|
||||
let multiple_instances = system.processes_by_exact_name("pinnacle").count() > 1;
|
||||
|
||||
// If you are, append a suffix to the socket name
|
||||
let socket_name = if multiple_instances {
|
||||
let mut suffix: u8 = 1;
|
||||
while let Ok(true) = socket_dir
|
||||
.join(format!("{SOCKET_NAME}_{suffix}"))
|
||||
.try_exists()
|
||||
{
|
||||
suffix += 1;
|
||||
}
|
||||
format!("{SOCKET_NAME}_{suffix}")
|
||||
} else {
|
||||
SOCKET_NAME.to_string()
|
||||
};
|
||||
|
||||
let socket_path = socket_dir.join(socket_name);
|
||||
|
||||
// If there are multiple instances, don't touch other sockets
|
||||
if multiple_instances {
|
||||
if let Ok(exists) = socket_path.try_exists() {
|
||||
if exists {
|
||||
std::fs::remove_file(&socket_path).context(format!(
|
||||
"Failed to remove old socket at {}",
|
||||
socket_path.to_string_lossy()
|
||||
))?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there are, remove them all
|
||||
for entry in WalkDir::new(socket_dir)
|
||||
.contents_first(true)
|
||||
.into_iter()
|
||||
.filter_entry(|entry| entry.file_name().to_string_lossy().starts_with(SOCKET_NAME))
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
std::fs::remove_file(entry.path()).context(format!(
|
||||
"Failed to remove old socket at {}",
|
||||
entry.path().to_string_lossy()
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use itertools::{Either, Itertools};
|
||||
use smithay::{
|
||||
desktop::layer_map_for_output,
|
||||
output::Output,
|
||||
|
@ -401,14 +400,17 @@ fn corner(layout: &Layout, windows: Vec<WindowElement>, rect: Rectangle<i32, Log
|
|||
_ => {
|
||||
let mut windows = windows.into_iter();
|
||||
let Some(corner) = windows.next() else { unreachable!() };
|
||||
let (horiz_stack, vert_stack): (Vec<WindowElement>, Vec<WindowElement>) =
|
||||
windows.enumerate().partition_map(|(i, win)| {
|
||||
if i % 2 == 0 {
|
||||
Either::Left(win)
|
||||
} else {
|
||||
Either::Right(win)
|
||||
}
|
||||
});
|
||||
|
||||
let mut horiz_stack = Vec::<WindowElement>::new();
|
||||
let mut vert_stack = Vec::<WindowElement>::new();
|
||||
|
||||
for (i, win) in windows.enumerate() {
|
||||
if i % 2 == 0 {
|
||||
horiz_stack.push(win);
|
||||
} else {
|
||||
vert_stack.push(win);
|
||||
}
|
||||
}
|
||||
|
||||
let div_factor = 2;
|
||||
|
||||
|
|
76
src/main.rs
76
src/main.rs
|
@ -11,7 +11,12 @@
|
|||
// #![deny(unused_imports)] // gonna force myself to keep stuff clean
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use clap::Parser;
|
||||
use tracing_subscriber::{fmt::writer::MakeWriterExt, EnvFilter};
|
||||
use walkdir::WalkDir;
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
mod api;
|
||||
mod backend;
|
||||
|
@ -29,6 +34,11 @@ mod state;
|
|||
mod tag;
|
||||
mod window;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref XDG_BASE_DIRS: BaseDirectories =
|
||||
BaseDirectories::with_prefix("pinnacle").expect("couldn't create xdg BaseDirectories");
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
#[group(id = "backend", required = false, multiple = false)]
|
||||
struct Backends {
|
||||
|
@ -53,18 +63,24 @@ struct Args {
|
|||
force: bool,
|
||||
}
|
||||
|
||||
const PINNACLE_LOG_PREFIX: &str = "pinnacle.log";
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
match tracing_subscriber::EnvFilter::try_from_default_env() {
|
||||
Ok(env_filter) => {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_env_filter(env_filter)
|
||||
.init();
|
||||
}
|
||||
Err(_) => {
|
||||
tracing_subscriber::fmt().compact().init();
|
||||
}
|
||||
}
|
||||
let xdg_state_dir = XDG_BASE_DIRS.get_state_home();
|
||||
|
||||
trim_logs(&xdg_state_dir);
|
||||
|
||||
let appender = tracing_appender::rolling::hourly(&xdg_state_dir, PINNACLE_LOG_PREFIX);
|
||||
let (appender, _guard) = tracing_appender::non_blocking(appender);
|
||||
let writer = appender.and(std::io::stdout);
|
||||
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("debug"));
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_env_filter(env_filter)
|
||||
.with_writer(writer)
|
||||
.init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
|
@ -124,3 +140,41 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn trim_logs(log_path: impl AsRef<Path>) {
|
||||
let logs = WalkDir::new(log_path)
|
||||
.sort_by(|a, b| {
|
||||
let a_creation_time = a
|
||||
.metadata()
|
||||
.expect("failed to get log metadata")
|
||||
.created()
|
||||
.expect("failed to get log creation time");
|
||||
let b_creation_time = b
|
||||
.metadata()
|
||||
.expect("failed to get log metadata")
|
||||
.created()
|
||||
.expect("failed to get log creation time");
|
||||
|
||||
a_creation_time.cmp(&b_creation_time)
|
||||
})
|
||||
.contents_first(true)
|
||||
.into_iter()
|
||||
.filter_entry(|entry| {
|
||||
entry.file_type().is_file()
|
||||
&& entry
|
||||
.file_name()
|
||||
.to_string_lossy()
|
||||
.starts_with(PINNACLE_LOG_PREFIX)
|
||||
})
|
||||
.filter_map(|dir| dir.ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If there are more than 4 logs, delete all but 3
|
||||
if logs.len() > 4 {
|
||||
let num_to_delete = logs.len().saturating_sub(3);
|
||||
for entry in logs.into_iter().take(num_to_delete) {
|
||||
tracing::info!("Deleting {:?}", entry.path());
|
||||
std::fs::remove_file(entry.path()).expect("failed to remove oldest log file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
45
src/state.rs
45
src/state.rs
|
@ -16,7 +16,7 @@ use crate::{
|
|||
window_rules::{WindowRule, WindowRuleCondition},
|
||||
CallbackId, ModifierMask, Msg,
|
||||
},
|
||||
PinnacleSocketSource, DEFAULT_SOCKET_DIR,
|
||||
PinnacleSocketSource,
|
||||
},
|
||||
backend::{udev::Udev, winit::Winit, BackendData},
|
||||
cursor::Cursor,
|
||||
|
@ -204,27 +204,26 @@ impl State {
|
|||
let (tx_channel, rx_channel) = calloop::channel::channel::<Msg>();
|
||||
|
||||
let config_dir = get_config_dir();
|
||||
tracing::debug!("config dir is {:?}", config_dir);
|
||||
|
||||
let metaconfig = crate::metaconfig::parse(&config_dir)?;
|
||||
|
||||
let socket_dir = {
|
||||
let dir_string = shellexpand::full(
|
||||
metaconfig
|
||||
.socket_dir
|
||||
.as_deref()
|
||||
.unwrap_or(DEFAULT_SOCKET_DIR),
|
||||
)?
|
||||
.to_string();
|
||||
|
||||
// If a socket is provided in the metaconfig, use it.
|
||||
let socket_dir = if let Some(socket_dir) = &metaconfig.socket_dir {
|
||||
// cd into the metaconfig dir and canonicalize to preserve relative paths
|
||||
// like ./dir/here
|
||||
let current_dir = std::env::current_dir()?;
|
||||
|
||||
std::env::set_current_dir(&config_dir)?;
|
||||
let pathbuf = PathBuf::from(&dir_string).canonicalize()?;
|
||||
let socket_dir = PathBuf::from(socket_dir).canonicalize()?;
|
||||
std::env::set_current_dir(current_dir)?;
|
||||
|
||||
pathbuf
|
||||
socket_dir
|
||||
} else {
|
||||
// Otherwise, use $XDG_RUNTIME_DIR. If that doesn't exist, use /tmp.
|
||||
crate::XDG_BASE_DIRS
|
||||
.get_runtime_directory()
|
||||
.cloned()
|
||||
.unwrap_or(PathBuf::from(crate::api::DEFAULT_SOCKET_DIR))
|
||||
};
|
||||
|
||||
let socket_source = PinnacleSocketSource::new(tx_channel, &socket_dir)
|
||||
|
@ -401,17 +400,11 @@ impl State {
|
|||
}
|
||||
|
||||
fn get_config_dir() -> PathBuf {
|
||||
let config_dir = std::env::var("PINNACLE_CONFIG_DIR").unwrap_or_else(|_| {
|
||||
let default_config_dir =
|
||||
std::env::var("XDG_CONFIG_HOME").unwrap_or("~/.config".to_string());
|
||||
let config_dir = std::env::var("PINNACLE_CONFIG_DIR")
|
||||
.ok()
|
||||
.and_then(|s| Some(PathBuf::from(shellexpand::full(&s).ok()?.to_string())));
|
||||
|
||||
PathBuf::from(default_config_dir)
|
||||
.join("pinnacle")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
});
|
||||
|
||||
PathBuf::from(shellexpand::tilde(&config_dir).to_string())
|
||||
config_dir.unwrap_or(crate::XDG_BASE_DIRS.get_config_home())
|
||||
}
|
||||
|
||||
/// This should be called *after* you have created the [`PinnacleSocketSource`] to ensure
|
||||
|
@ -422,7 +415,9 @@ fn start_config(metaconfig: Metaconfig, config_dir: &Path) -> anyhow::Result<Con
|
|||
|
||||
let mut command = metaconfig.command.split(' ');
|
||||
|
||||
let arg1 = command.next().expect("empty command");
|
||||
let arg1 = command
|
||||
.next()
|
||||
.context("command in metaconfig.toml was empty")?;
|
||||
|
||||
std::env::set_var("PINNACLE_DIR", std::env::current_dir()?);
|
||||
|
||||
|
@ -457,6 +452,8 @@ fn start_config(metaconfig: Metaconfig, config_dir: &Path) -> anyhow::Result<Con
|
|||
.args(command)
|
||||
.envs(envs)
|
||||
.current_dir(config_dir)
|
||||
.stdout(async_process::Stdio::inherit())
|
||||
.stderr(async_process::Stdio::inherit())
|
||||
.spawn()
|
||||
.expect("failed to spawn config");
|
||||
|
||||
|
|
Loading…
Reference in a new issue