2023-08-01 18:06:35 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
2023-06-22 01:58:49 +02:00
//! A very, VERY WIP Smithay-based Wayland compositor.
//!
//! Pinnacle is heavily inspired by the [Awesome Window Manager](https://awesomewm.org),
//! and this is an attempt to make something akin to it for Wayland.
//!
2023-06-22 02:08:29 +02:00
//! While Pinnacle is not a library, this documentation serves to guide those who want to
2023-06-22 01:58:49 +02:00
//! contribute or learn how building something like this works.
2023-09-09 05:08:56 +02:00
// #![deny(unused_imports)] // gonna force myself to keep stuff clean
2023-06-30 00:41:08 +02:00
#![ warn(clippy::unwrap_used) ]
2023-06-29 19:29:00 +02:00
2023-09-11 09:48:33 +02:00
use std ::path ::Path ;
2023-09-08 07:21:09 +02:00
use clap ::Parser ;
2023-09-11 09:48:33 +02:00
use tracing_subscriber ::{ fmt ::writer ::MakeWriterExt , EnvFilter } ;
use walkdir ::WalkDir ;
use xdg ::BaseDirectories ;
2023-09-08 07:21:09 +02:00
2023-06-18 01:55:04 +02:00
mod api ;
2023-06-12 00:56:34 +02:00
mod backend ;
mod cursor ;
2023-06-18 04:02:58 +02:00
mod focus ;
2023-06-12 00:56:34 +02:00
mod grab ;
mod handlers ;
mod input ;
mod layout ;
2023-08-15 03:19:38 +02:00
mod metaconfig ;
2023-06-26 03:26:52 +02:00
mod output ;
2023-06-12 00:56:34 +02:00
mod pointer ;
mod render ;
mod state ;
2023-07-01 04:34:07 +02:00
mod tag ;
2023-06-12 00:56:34 +02:00
mod window ;
2023-09-11 09:48:33 +02:00
lazy_static ::lazy_static! {
pub static ref XDG_BASE_DIRS : BaseDirectories =
BaseDirectories ::with_prefix ( " pinnacle " ) . expect ( " couldn't create xdg BaseDirectories " ) ;
}
2023-09-08 07:21:09 +02:00
#[ derive(clap::Args, Debug) ]
2023-09-09 03:20:00 +02:00
#[ group(id = " backend " , required = false, multiple = false) ]
2023-09-08 07:21:09 +02:00
struct Backends {
#[ arg(long, group = " backend " ) ]
/// Run Pinnacle in a window in your graphical environment
winit : bool ,
#[ arg(long, group = " backend " ) ]
/// Run Pinnacle from a tty
udev : bool ,
}
#[ derive(clap::Parser, Debug) ]
#[ command(author, version, about, long_about = None) ]
struct Args {
#[ command(flatten) ]
backend : Backends ,
#[ arg(long) ]
/// Allow running Pinnacle as root (this is NOT recommended)
allow_root : bool ,
#[ arg(long, requires = " backend " ) ]
/// Force Pinnacle to run with the provided backend
force : bool ,
}
2023-09-11 09:48:33 +02:00
const PINNACLE_LOG_PREFIX : & str = " pinnacle.log " ;
2023-08-16 18:28:35 +02:00
fn main ( ) -> anyhow ::Result < ( ) > {
2023-09-11 09:48:33 +02:00
let xdg_state_dir = XDG_BASE_DIRS . get_state_home ( ) ;
trim_logs ( & xdg_state_dir ) ;
2023-09-11 10:05:45 +02:00
let appender = tracing_appender ::rolling ::hourly ( & xdg_state_dir , PINNACLE_LOG_PREFIX ) ;
2023-09-11 09:48:33 +02:00
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 ( ) ;
2023-06-15 19:42:34 +02:00
2023-09-08 07:21:09 +02:00
let args = Args ::parse ( ) ;
if smithay ::reexports ::nix ::unistd ::Uid ::effective ( ) . is_root ( ) & & ! args . allow_root {
println! ( " You are trying to run Pinnacle as root. \n This is NOT recommended. \n To run Pinnacle as root, pass in the --allow-root flag. Again, this is NOT recommended. " ) ;
return Ok ( ( ) ) ;
}
2023-08-08 20:25:47 +02:00
let in_graphical_env =
std ::env ::var ( " WAYLAND_DISPLAY " ) . is_ok ( ) | | std ::env ::var ( " DISPLAY " ) . is_ok ( ) ;
2023-09-08 07:21:09 +02:00
match ( args . backend . winit , args . backend . udev , args . force ) {
( false , false , _ ) = > {
if in_graphical_env {
tracing ::info! ( " Starting winit backend " ) ;
crate ::backend ::winit ::run_winit ( ) ? ;
} else {
tracing ::info! ( " Starting udev backend " ) ;
crate ::backend ::udev ::run_udev ( ) ? ;
}
}
( true , false , force ) = > {
2023-08-08 20:25:47 +02:00
if ! in_graphical_env {
2023-09-08 07:21:09 +02:00
if force {
tracing ::warn! ( " Starting winit backend with no detected graphical environment " ) ;
2023-08-08 20:25:47 +02:00
crate ::backend ::winit ::run_winit ( ) ? ;
} else {
2023-09-08 07:21:09 +02:00
println! ( " Both WAYLAND_DISPLAY and DISPLAY are not set. " ) ;
2023-08-08 20:25:47 +02:00
println! ( " If you are trying to run the winit backend in a tty, it won't work. " ) ;
println! ( " If you really want to, additionally pass in the --force flag. " ) ;
}
} else {
tracing ::info! ( " Starting winit backend " ) ;
crate ::backend ::winit ::run_winit ( ) ? ;
}
2023-06-19 20:27:54 +02:00
}
2023-09-08 07:21:09 +02:00
( false , true , force ) = > {
2023-08-08 20:25:47 +02:00
if in_graphical_env {
2023-09-08 07:21:09 +02:00
if force {
tracing ::warn! ( " Starting udev backend with a detected graphical environment " ) ;
2023-08-08 20:25:47 +02:00
crate ::backend ::udev ::run_udev ( ) ? ;
} else {
2023-09-08 07:21:09 +02:00
println! ( " WAYLAND_DISPLAY and/or DISPLAY are set. " ) ;
2023-08-08 20:25:47 +02:00
println! (
" If you are trying to run the udev backend in a graphical environment, "
) ;
println! ( " it won't work and may mess some things up. " ) ;
println! ( " If you really want to, additionally pass in the --force flag. " ) ;
}
} else {
tracing ::info! ( " Starting udev backend " ) ;
crate ::backend ::udev ::run_udev ( ) ? ;
}
2023-06-19 20:27:54 +02:00
}
2023-09-08 07:21:09 +02:00
_ = > unreachable! ( ) ,
2023-06-19 19:42:49 +02:00
}
2023-06-12 00:56:34 +02:00
Ok ( ( ) )
}
2023-09-11 09:48:33 +02:00
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 )
} )
2023-09-11 10:05:45 +02:00
. contents_first ( true )
2023-09-11 09:48:33 +02:00
. 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 < _ > > ( ) ;
2023-09-11 10:05:45 +02:00
// 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 " ) ;
}
2023-09-11 09:48:33 +02:00
}
}