mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-14 08:01:14 +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+"
|
license = "GPL-3.0+"
|
||||||
description = "A WIP Smithay-based Wayland compositor, inspired by AwesomeWM"
|
description = "A WIP Smithay-based Wayland compositor, inspired by AwesomeWM"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/Ottatop/pinnacle/"
|
repository = "https://github.com/pinnacle-comp/pinnacle/"
|
||||||
keywords = ["wayland", "compositor", "smithay", "lua"]
|
keywords = ["wayland", "compositor", "smithay", "lua"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
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"] }
|
calloop = { version = "0.10.1", features = ["executor", "futures-io"] }
|
||||||
futures-lite = { version = "1.13.0" }
|
futures-lite = { version = "1.13.0" }
|
||||||
async-process = { version = "1.7.0" }
|
async-process = { version = "1.7.0" }
|
||||||
itertools = { version = "0.11.0" }
|
|
||||||
x11rb = { version = "0.12.0", default-features = false, features = ["composite"], optional = true }
|
x11rb = { version = "0.12.0", default-features = false, features = ["composite"], optional = true }
|
||||||
shellexpand = "3.1.0"
|
shellexpand = "3.1.0"
|
||||||
toml = "0.7.7"
|
toml = "0.7.7"
|
||||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||||
clap = { version = "4.4.2", features = ["derive"] }
|
clap = { version = "4.4.2", features = ["derive"] }
|
||||||
xkbcommon = "0.6.0"
|
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]
|
[features]
|
||||||
default = ["egl", "winit", "udev", "xwayland"]
|
default = ["egl", "winit", "udev", "xwayland"]
|
||||||
|
|
51
src/api.rs
51
src/api.rs
|
@ -46,10 +46,13 @@ use anyhow::Context;
|
||||||
use smithay::reexports::calloop::{
|
use smithay::reexports::calloop::{
|
||||||
self, channel::Sender, generic::Generic, EventSource, Interest, Mode, PostAction,
|
self, channel::Sender, generic::Generic, EventSource, Interest, Mode, PostAction,
|
||||||
};
|
};
|
||||||
|
use sysinfo::{ProcessRefreshKind, RefreshKind, SystemExt};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use self::msg::{Msg, OutgoingMsg};
|
use self::msg::{Msg, OutgoingMsg};
|
||||||
|
|
||||||
pub const DEFAULT_SOCKET_DIR: &str = "/tmp";
|
pub const DEFAULT_SOCKET_DIR: &str = "/tmp";
|
||||||
|
pub const SOCKET_NAME: &str = "pinnacle_socket";
|
||||||
|
|
||||||
fn handle_client(
|
fn handle_client(
|
||||||
mut stream: UnixStream,
|
mut stream: UnixStream,
|
||||||
|
@ -90,11 +93,51 @@ impl PinnacleSocketSource {
|
||||||
/// Create a loop source that listens for connections to the provided socket_dir.
|
/// Create a loop source that listens for connections to the provided socket_dir.
|
||||||
/// This will also set PINNACLE_SOCKET for use in API implementations.
|
/// This will also set PINNACLE_SOCKET for use in API implementations.
|
||||||
pub fn new(sender: Sender<Msg>, socket_dir: &Path) -> anyhow::Result<Self> {
|
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() {
|
// Test if you are running multiple instances of Pinnacle
|
||||||
if exists {
|
let multiple_instances = system.processes_by_exact_name("pinnacle").count() > 1;
|
||||||
std::fs::remove_file(&socket_path).context("Failed to remove old socket")?;
|
|
||||||
|
// 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
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
use itertools::{Either, Itertools};
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::layer_map_for_output,
|
desktop::layer_map_for_output,
|
||||||
output::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 mut windows = windows.into_iter();
|
||||||
let Some(corner) = windows.next() else { unreachable!() };
|
let Some(corner) = windows.next() else { unreachable!() };
|
||||||
let (horiz_stack, vert_stack): (Vec<WindowElement>, Vec<WindowElement>) =
|
|
||||||
windows.enumerate().partition_map(|(i, win)| {
|
let mut horiz_stack = Vec::<WindowElement>::new();
|
||||||
if i % 2 == 0 {
|
let mut vert_stack = Vec::<WindowElement>::new();
|
||||||
Either::Left(win)
|
|
||||||
} else {
|
for (i, win) in windows.enumerate() {
|
||||||
Either::Right(win)
|
if i % 2 == 0 {
|
||||||
}
|
horiz_stack.push(win);
|
||||||
});
|
} else {
|
||||||
|
vert_stack.push(win);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let div_factor = 2;
|
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
|
// #![deny(unused_imports)] // gonna force myself to keep stuff clean
|
||||||
#![warn(clippy::unwrap_used)]
|
#![warn(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use tracing_subscriber::{fmt::writer::MakeWriterExt, EnvFilter};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod backend;
|
mod backend;
|
||||||
|
@ -29,6 +34,11 @@ mod state;
|
||||||
mod tag;
|
mod tag;
|
||||||
mod window;
|
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)]
|
#[derive(clap::Args, Debug)]
|
||||||
#[group(id = "backend", required = false, multiple = false)]
|
#[group(id = "backend", required = false, multiple = false)]
|
||||||
struct Backends {
|
struct Backends {
|
||||||
|
@ -53,18 +63,24 @@ struct Args {
|
||||||
force: bool,
|
force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PINNACLE_LOG_PREFIX: &str = "pinnacle.log";
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
match tracing_subscriber::EnvFilter::try_from_default_env() {
|
let xdg_state_dir = XDG_BASE_DIRS.get_state_home();
|
||||||
Ok(env_filter) => {
|
|
||||||
tracing_subscriber::fmt()
|
trim_logs(&xdg_state_dir);
|
||||||
.compact()
|
|
||||||
.with_env_filter(env_filter)
|
let appender = tracing_appender::rolling::hourly(&xdg_state_dir, PINNACLE_LOG_PREFIX);
|
||||||
.init();
|
let (appender, _guard) = tracing_appender::non_blocking(appender);
|
||||||
}
|
let writer = appender.and(std::io::stdout);
|
||||||
Err(_) => {
|
|
||||||
tracing_subscriber::fmt().compact().init();
|
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();
|
let args = Args::parse();
|
||||||
|
|
||||||
|
@ -124,3 +140,41 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
Ok(())
|
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},
|
window_rules::{WindowRule, WindowRuleCondition},
|
||||||
CallbackId, ModifierMask, Msg,
|
CallbackId, ModifierMask, Msg,
|
||||||
},
|
},
|
||||||
PinnacleSocketSource, DEFAULT_SOCKET_DIR,
|
PinnacleSocketSource,
|
||||||
},
|
},
|
||||||
backend::{udev::Udev, winit::Winit, BackendData},
|
backend::{udev::Udev, winit::Winit, BackendData},
|
||||||
cursor::Cursor,
|
cursor::Cursor,
|
||||||
|
@ -204,27 +204,26 @@ impl State {
|
||||||
let (tx_channel, rx_channel) = calloop::channel::channel::<Msg>();
|
let (tx_channel, rx_channel) = calloop::channel::channel::<Msg>();
|
||||||
|
|
||||||
let config_dir = get_config_dir();
|
let config_dir = get_config_dir();
|
||||||
|
tracing::debug!("config dir is {:?}", config_dir);
|
||||||
|
|
||||||
let metaconfig = crate::metaconfig::parse(&config_dir)?;
|
let metaconfig = crate::metaconfig::parse(&config_dir)?;
|
||||||
|
|
||||||
let socket_dir = {
|
// If a socket is provided in the metaconfig, use it.
|
||||||
let dir_string = shellexpand::full(
|
let socket_dir = if let Some(socket_dir) = &metaconfig.socket_dir {
|
||||||
metaconfig
|
|
||||||
.socket_dir
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or(DEFAULT_SOCKET_DIR),
|
|
||||||
)?
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// cd into the metaconfig dir and canonicalize to preserve relative paths
|
// cd into the metaconfig dir and canonicalize to preserve relative paths
|
||||||
// like ./dir/here
|
// like ./dir/here
|
||||||
let current_dir = std::env::current_dir()?;
|
let current_dir = std::env::current_dir()?;
|
||||||
|
|
||||||
std::env::set_current_dir(&config_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)?;
|
std::env::set_current_dir(current_dir)?;
|
||||||
|
socket_dir
|
||||||
pathbuf
|
} 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)
|
let socket_source = PinnacleSocketSource::new(tx_channel, &socket_dir)
|
||||||
|
@ -401,17 +400,11 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_dir() -> PathBuf {
|
fn get_config_dir() -> PathBuf {
|
||||||
let config_dir = std::env::var("PINNACLE_CONFIG_DIR").unwrap_or_else(|_| {
|
let config_dir = std::env::var("PINNACLE_CONFIG_DIR")
|
||||||
let default_config_dir =
|
.ok()
|
||||||
std::env::var("XDG_CONFIG_HOME").unwrap_or("~/.config".to_string());
|
.and_then(|s| Some(PathBuf::from(shellexpand::full(&s).ok()?.to_string())));
|
||||||
|
|
||||||
PathBuf::from(default_config_dir)
|
config_dir.unwrap_or(crate::XDG_BASE_DIRS.get_config_home())
|
||||||
.join("pinnacle")
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
PathBuf::from(shellexpand::tilde(&config_dir).to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This should be called *after* you have created the [`PinnacleSocketSource`] to ensure
|
/// 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 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()?);
|
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)
|
.args(command)
|
||||||
.envs(envs)
|
.envs(envs)
|
||||||
.current_dir(config_dir)
|
.current_dir(config_dir)
|
||||||
|
.stdout(async_process::Stdio::inherit())
|
||||||
|
.stderr(async_process::Stdio::inherit())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("failed to spawn config");
|
.expect("failed to spawn config");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue