diff --git a/src/backend/dummy.rs b/src/backend/dummy.rs index d46ae37..704bbcc 100644 --- a/src/backend/dummy.rs +++ b/src/backend/dummy.rs @@ -31,15 +31,10 @@ pub struct Dummy { } impl Backend { - fn dummy_mut(&mut self) -> &mut Dummy { + fn dummy_mut(&mut self) -> &Dummy { let Backend::Dummy(dummy) = self else { unreachable!() }; dummy } - - #[cfg(feature = "wlcs")] - pub fn wlcs_mut(&mut self) -> &mut Wlcs { - &mut self.dummy_mut().wlcs_state - } } impl BackendData for Dummy { @@ -121,7 +116,7 @@ pub fn setup_dummy( state.space.map_output(&output, (0, 0)); - /* if let Err(err) = state.xwayland.start( + if let Err(err) = state.xwayland.start( state.loop_handle.clone(), None, std::iter::empty::<(OsString, OsString)>(), @@ -129,7 +124,7 @@ pub fn setup_dummy( |_| {}, ) { tracing::error!("Failed to start XWayland: {err}"); - } */ + } Ok((state, event_loop)) } diff --git a/src/backend/wlcs.rs b/src/backend/wlcs.rs index e290b03..c736c0b 100644 --- a/src/backend/wlcs.rs +++ b/src/backend/wlcs.rs @@ -1,9 +1,89 @@ -use std::{collections::HashMap, sync::{atomic::AtomicBool, Arc}}; +use std::{collections::HashMap, path::PathBuf}; -use smithay::reexports::wayland_server::Client; +use smithay::{ + backend::renderer::{test::DummyRenderer, ImportMemWl}, + output::{Output, Subpixel}, + reexports::{ + calloop::EventLoop, + wayland_server::{Client, Display}, + }, + utils::Transform, +}; + +use crate::state::State; + +use super::{dummy::Dummy, Backend}; #[derive(Default)] pub struct Wlcs { pub clients: HashMap, - pub running: Arc, +} + +impl Backend { + pub fn wlcs_mut(&mut self) -> &mut Wlcs { + let Backend::Dummy(dummy) = self else { unreachable!() }; + &mut dummy.wlcs_state + } +} + +pub fn setup_wlcs_dummy( + no_config: bool, + config_dir: Option, +) -> anyhow::Result<(State, EventLoop<'static, State>)> { + let event_loop: EventLoop = EventLoop::try_new()?; + + let display: Display = Display::new()?; + let display_handle = display.handle(); + + let loop_handle = event_loop.handle(); + + let mode = smithay::output::Mode { + size: (1920, 1080).into(), + refresh: 60_000, + }; + + let physical_properties = smithay::output::PhysicalProperties { + size: (0, 0).into(), + subpixel: Subpixel::Unknown, + make: "Pinnacle".to_string(), + model: "Dummy Window".to_string(), + }; + + let output = Output::new("Pinnacle Window".to_string(), physical_properties); + + output.create_global::(&display_handle); + + output.change_current_state( + Some(mode), + Some(Transform::Flipped180), + None, + Some((0, 0).into()), + ); + + output.set_preferred(mode); + + let renderer = DummyRenderer::new(); + let shm_formats = renderer.shm_formats(); + + let backend = Dummy { + renderer, + wlcs_state: Wlcs::default(), + }; + + let mut state = State::init( + super::Backend::Dummy(backend), + display, + event_loop.get_signal(), + loop_handle, + no_config, + config_dir, + )?; + + state.output_focus_stack.set_focus(output.clone()); + + state.shm_state.update_formats(shm_formats); + + state.space.map_output(&output, (0, 0)); + + Ok((state, event_loop)) } diff --git a/src/config.rs b/src/config.rs index 7a56042..918f15a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -564,17 +564,10 @@ impl State { } })); } - None => { - self.grpc_server_join_handle = Some(tokio::spawn(async move { - if let Err(err) = grpc_server.serve_with_incoming(uds_stream).await { - error!("gRPC server error: {err}"); - } - })); - } // FIXME: Not really high priority but if you somehow reload the config really, REALLY // | fast at startup then I think there's a chance that the gRPC server // | could get started twice. - /* None => self.schedule( + None => self.schedule( |state| state.xdisplay.is_some(), move |state| { state.grpc_server_join_handle = Some(tokio::spawn(async move { @@ -583,7 +576,7 @@ impl State { } })); }, - ), */ + ), } Ok(()) diff --git a/wlcs_pinnacle/config/metaconfig.toml b/wlcs_pinnacle/config/metaconfig.toml index 2afc257..4309020 100644 --- a/wlcs_pinnacle/config/metaconfig.toml +++ b/wlcs_pinnacle/config/metaconfig.toml @@ -1,46 +1,6 @@ -# This metaconfig.toml file dictates what config Pinnacle will run. -# -# When running Pinnacle, the compositor will look in the following directories for a metaconfig.toml file, -# in order from top to bottom: -# $PINNACLE_CONFIG_DIR -# $XDG_CONFIG_HOME/pinnacle/ -# ~/.config/pinnacle/ -# -# When Pinnacle finds a metaconfig.toml file, it will execute the command provided to `command`. -# To use a Rust config, this should be changed to something like ["cargo", "run"]. -# -# Because configuration is done using an external process, if it ever crashes, you lose all of your keybinds. -# The compositor will load the default config if that happens, but in the event that you don't have -# the necessary dependencies for it to run, you may get softlocked. -# In order prevent you from getting stuck in the compositor, you must define keybinds to reload your config -# and kill Pinnacle. -# -# More details on each setting can be found below. - -# The command Pinnacle will run on startup and when you reload your config. -# Paths are relative to the directory the metaconfig.toml file is in. -# This must be an array. command = ["./pinnacle-config"] -### Keybinds ### -# Each keybind takes in a table with two fields: `modifiers` and `key`. -# - `modifiers` can be one of "Ctrl", "Alt", "Shift", or "Super". -# - `key` can be a string of any lowercase letter, number, -# "numN" where N is a number for numpad keys, or "esc"/"escape". -# Support for any xkbcommon key is planned for a future update. - -# The keybind that will reload your config. reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" } -# The keybind that will kill Pinnacle. kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" } -### Socket directory ### -# Pinnacle will open a Unix socket at `$XDG_RUNTIME_DIR` by default, falling back to `/tmp` if it doesn't exist. -# If you want/need to change this, use the `socket_dir` setting set to the directory of your choosing. -# -# socket_dir = "/your/dir/here/" - -### Environment Variables ### -# If you need to spawn your config with any environment variables, list them here. [envs] -# key = "value" diff --git a/wlcs_pinnacle/config/src/main.rs b/wlcs_pinnacle/config/src/main.rs index 6e47f45..d7330f3 100644 --- a/wlcs_pinnacle/config/src/main.rs +++ b/wlcs_pinnacle/config/src/main.rs @@ -1,36 +1,12 @@ use pinnacle_api::layout::{CyclingLayoutManager, MasterStackLayout}; use pinnacle_api::ApiModules; -// Pinnacle needs to perform some setup before and after your config. -// The `#[pinnacle_api::config(modules)]` attribute does so and -// will bind all the config structs to the provided identifier. #[pinnacle_api::config(modules)] async fn main() { - // Deconstruct to get all the APIs. #[allow(unused_variables)] - let ApiModules { - pinnacle, - process, - window, - input, - output, - tag, - layout, - render, - } = modules; + let ApiModules { layout, .. } = modules; let _layout_requester = layout.set_manager(CyclingLayoutManager::new([ Box::::default() as _, ])); - - // Setup all monitors with tags "1" through "5" - output.connect_for_all(move |op| { - let tags = tag.add(op, ["tag"]); - tags.first().unwrap().set_active(true); - }); - - // Enable sloppy focus - /* window.connect_signal(WindowSignal::PointerEnter(Box::new(|win| { - win.set_focused(true); - }))); */ } diff --git a/wlcs_pinnacle/src/lib.rs b/wlcs_pinnacle/src/lib.rs index 1e7e608..dde658e 100644 --- a/wlcs_pinnacle/src/lib.rs +++ b/wlcs_pinnacle/src/lib.rs @@ -202,22 +202,28 @@ impl Wlcs for PinnacleHandle { } fn create_pointer(&mut self) -> Option { + let device_id = new_device_id(); self.server_conn .as_ref() .map(|conn| conn.sender.clone()) - .map(|sender| PointerHandle { - device_id: new_device_id(), - sender, + .map(|sender| { + sender + .send(WlcsEvent::NewPointer { device_id }) + .expect("failed to send new_pointer"); + PointerHandle { device_id, sender } }) } fn create_touch(&mut self) -> Option { + let device_id = new_device_id(); self.server_conn .as_ref() .map(|conn| conn.sender.clone()) - .map(|sender| TouchHandle { - device_id: new_device_id(), - sender, + .map(|sender| { + sender + .send(WlcsEvent::NewTouch { device_id }) + .expect("failed to send new_touch"); + TouchHandle { device_id, sender } }) } diff --git a/wlcs_pinnacle/src/main_loop.rs b/wlcs_pinnacle/src/main_loop.rs index e6a602e..f5b5480 100644 --- a/wlcs_pinnacle/src/main_loop.rs +++ b/wlcs_pinnacle/src/main_loop.rs @@ -1,14 +1,14 @@ use std::{sync::Arc, time::Duration}; use pinnacle::{ - backend::dummy::setup_dummy, + backend::wlcs::setup_wlcs_dummy, state::{ClientState, State}, }; use smithay::{ backend::input::{ButtonState, DeviceCapability, InputEvent}, reexports::{ calloop::channel::{Channel, Event}, - wayland_server::Resource, + wayland_server::{Client, Resource}, }, wayland::seat::WaylandFocus, }; @@ -26,7 +26,7 @@ pub(crate) fn run(channel: Channel) { &std::env::var("PINNACLE_WLCS_CONFIG_PATH").expect("PINNACLE_WLCS_CONFIG_PATH not set"); let (mut state, mut event_loop) = - setup_dummy(false, Some(config_path.into())).expect("failed to setup dummy backend"); + setup_wlcs_dummy(false, Some(config_path.into())).expect("failed to setup dummy backend"); event_loop .handle() @@ -40,19 +40,27 @@ pub(crate) fn run(channel: Channel) { let rt = tokio::runtime::Runtime::new().expect("failed to create tokio runtime"); let _handle = rt.enter(); + // FIXME: once starting pinnacle without xwayland is a thing, handle this + // | properly; in this case, we probably no longer need to start the + // | config manually anymore either, as this is only needed now, + // | because the config is started after xwayland reports its ready + + // when xdiplay is None when starting the config, the grpc server is not + // started, until it is set; this bypasses this for now + state.xdisplay = Some(u32::MAX); if let Err(err) = state.start_config(config_path) { panic!("failed to start config: {err}"); } - // FIXME: different sock_dir per instance? + // FIXME: use a custom socker_dir to avoid having to number sockets + + // wait for the config to connect to the layout service while state.layout_state.layout_request_sender.is_none() { event_loop .dispatch(Some(Duration::from_millis(10)), &mut state) .expect("event_loop error while waiting for config"); } - // TODO: handle no-xwayland properly - event_loop .run(None, &mut state, |state| { state.update_pointer_focus(); @@ -72,7 +80,7 @@ fn handle_event(event: WlcsEvent, state: &mut State) { match event { WlcsEvent::Stop => state.shutdown(), WlcsEvent::NewClient { stream, client_id } => { - let client: smithay::reexports::wayland_server::Client = state + let client: Client = state .display_handle .insert_client(stream, Arc::new(ClientState::default())) .expect("failed to insert new client");