pinnacle/src/state.rs

1010 lines
37 KiB
Rust
Raw Normal View History

2023-06-25 17:18:50 -05:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
2023-06-25 17:49:06 -05:00
//
// SPDX-License-Identifier: MPL-2.0
2023-06-25 17:18:50 -05:00
2023-06-17 18:55:04 -05:00
use std::{
cell::RefCell,
2023-06-17 18:55:04 -05:00
error::Error,
ffi::OsString,
2023-06-17 18:55:04 -05:00
os::{fd::AsRawFd, unix::net::UnixStream},
2023-07-02 18:15:44 -05:00
path::Path,
2023-06-21 14:48:38 -05:00
process::Stdio,
sync::{Arc, Mutex},
2023-06-17 18:55:04 -05:00
};
2023-06-17 21:02:58 -05:00
use crate::{
2023-06-21 14:48:38 -05:00
api::{
msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestResponse},
2023-06-21 14:48:38 -05:00
PinnacleSocketSource,
},
2023-06-17 21:02:58 -05:00
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
2023-07-18 15:12:23 -05:00
tag::Tag,
window::window_state::WindowResizeState,
2023-06-17 21:02:58 -05:00
};
use calloop::futures::Scheduler;
use futures_lite::AsyncBufReadExt;
2023-06-02 16:01:48 -05:00
use smithay::{
2023-06-09 20:29:17 -05:00
backend::renderer::element::RenderElementStates,
desktop::{
utils::{
surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
OutputPresentationFeedback,
},
PopupManager, Space, Window,
},
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
output::Output,
2023-06-02 16:01:48 -05:00
reexports::{
2023-06-17 18:55:04 -05:00
calloop::{
self, channel::Event, generic::Generic, Interest, LoopHandle, LoopSignal, Mode,
PostAction,
},
2023-06-02 16:01:48 -05:00
wayland_server::{
backend::{ClientData, ClientId, DisconnectReason},
protocol::wl_surface::WlSurface,
2023-06-02 16:01:48 -05:00
Display,
},
},
utils::{Clock, Logical, Monotonic, Point},
2023-06-02 16:01:48 -05:00
wayland::{
compositor::{self, CompositorClientState, CompositorState},
2023-06-02 16:01:48 -05:00
data_device::DataDeviceState,
2023-06-09 20:29:17 -05:00
dmabuf::DmabufFeedback,
fractional_scale::FractionalScaleManagerState,
2023-06-02 16:01:48 -05:00
output::OutputManagerState,
2023-07-18 21:10:43 -05:00
shell::xdg::{XdgShellState, XdgToplevelSurfaceData},
2023-06-02 16:01:48 -05:00
shm::ShmState,
2023-06-09 20:29:17 -05:00
socket::ListeningSocketSource,
viewporter::ViewporterState,
2023-06-02 16:01:48 -05:00
},
};
use crate::{backend::Backend, input::InputState};
2023-06-02 16:01:48 -05:00
2023-06-21 18:58:49 -05:00
/// The main state of the application.
2023-06-02 16:01:48 -05:00
pub struct State<B: Backend> {
pub backend_data: B,
2023-06-02 16:01:48 -05:00
pub loop_signal: LoopSignal,
2023-06-09 20:29:17 -05:00
pub loop_handle: LoopHandle<'static, CalloopData<B>>,
2023-06-02 16:01:48 -05:00
pub clock: Clock<Monotonic>,
pub space: Space<Window>,
pub move_mode: bool,
2023-06-09 20:29:17 -05:00
pub socket_name: String,
pub seat: Seat<State<B>>,
2023-06-02 16:01:48 -05:00
pub compositor_state: CompositorState,
pub data_device_state: DataDeviceState,
pub seat_state: SeatState<Self>,
pub shm_state: ShmState,
pub output_manager_state: OutputManagerState,
pub xdg_shell_state: XdgShellState,
pub viewporter_state: ViewporterState,
pub fractional_scale_manager_state: FractionalScaleManagerState,
pub input_state: InputState,
2023-06-17 18:55:04 -05:00
pub api_state: ApiState,
2023-06-17 21:02:58 -05:00
pub focus_state: FocusState,
2023-06-02 16:01:48 -05:00
pub popup_manager: PopupManager,
pub cursor_status: CursorImageStatus,
pub pointer_location: Point<f64, Logical>,
2023-06-30 21:34:07 -05:00
pub windows: Vec<Window>,
pub async_scheduler: Scheduler<()>,
2023-07-11 11:59:38 -05:00
// TODO: move into own struct
// | basically just clean this mess up
pub output_callback_ids: Vec<CallbackId>,
2023-06-02 16:01:48 -05:00
}
2023-06-05 21:08:37 -05:00
impl<B: Backend> State<B> {
2023-07-02 18:15:44 -05:00
pub fn handle_msg(&mut self, msg: Msg) {
2023-07-18 12:37:40 -05:00
// tracing::debug!("Got {msg:?}");
2023-07-02 18:15:44 -05:00
match msg {
Msg::SetKeybind {
key,
modifiers,
callback_id,
} => {
tracing::info!("set keybind: {:?}, {}", modifiers, key);
self.input_state
.keybinds
.insert((modifiers.into(), key), callback_id);
}
2023-07-18 15:12:23 -05:00
Msg::SetMousebind { button: _ } => todo!(),
Msg::CloseWindow { window_id } => {
2023-07-18 21:10:43 -05:00
if let Some(window) = window_id.window(self) {
2023-07-02 18:15:44 -05:00
window.toplevel().send_close();
}
}
2023-07-18 15:12:23 -05:00
Msg::ToggleFloating { window_id } => {
2023-07-18 21:10:43 -05:00
if let Some(window) = window_id.window(self) {
2023-07-02 18:15:44 -05:00
crate::window::toggle_floating(self, &window);
}
}
2023-06-09 20:29:17 -05:00
2023-07-02 18:15:44 -05:00
Msg::Spawn {
command,
callback_id,
} => {
self.handle_spawn(command, callback_id);
}
2023-07-18 21:10:43 -05:00
Msg::SetWindowSize {
window_id,
width,
height,
} => {
let Some(window) = window_id.window(self) else { return };
2023-06-09 20:29:17 -05:00
2023-07-02 18:15:44 -05:00
// TODO: tiled vs floating
2023-07-18 21:10:43 -05:00
let window_size = window.geometry().size;
2023-07-02 18:15:44 -05:00
window.toplevel().with_pending_state(|state| {
2023-07-18 21:10:43 -05:00
// INFO: calling window.geometry() in with_pending_state
// | will hang the compositor
state.size = Some(
(
width.unwrap_or(window_size.w),
height.unwrap_or(window_size.h),
)
.into(),
);
2023-07-02 18:15:44 -05:00
});
window.toplevel().send_pending_configure();
}
Msg::MoveWindowToTag { window_id, tag_id } => {
2023-07-18 21:10:43 -05:00
if let Some(window) = window_id.window(self) {
window.with_state(|state| {
self.focus_state
.focused_output
.as_ref()
.unwrap()
.with_state(|op_state| {
let tag = op_state.tags.iter().find(|tag| tag.name() == tag_id);
2023-07-09 17:48:46 -05:00
if let Some(tag) = tag {
state.tags = vec![tag.clone()];
2023-07-09 17:48:46 -05:00
}
});
2023-07-02 18:15:44 -05:00
});
2023-07-18 21:10:43 -05:00
let output = self.focus_state.focused_output.clone().unwrap();
self.re_layout(&output);
2023-07-02 18:15:44 -05:00
}
}
Msg::ToggleTagOnWindow { window_id, tag_id } => {
2023-07-18 21:10:43 -05:00
if let Some(window) = window_id.window(self) {
window.with_state(|state| {
self.focus_state
.focused_output
.as_ref()
.unwrap()
.with_state(|op_state| {
let tag = op_state.tags.iter().find(|tag| tag.name() == tag_id);
2023-07-09 17:48:46 -05:00
if let Some(tag) = tag {
2023-07-11 21:07:51 -05:00
if state.tags.contains(tag) {
state.tags.retain(|tg| tg != tag);
2023-07-09 17:48:46 -05:00
} else {
state.tags.push(tag.clone());
2023-07-09 17:48:46 -05:00
}
}
});
2023-07-02 18:15:44 -05:00
});
2023-06-21 14:48:38 -05:00
let output = self.focus_state.focused_output.clone().unwrap();
self.re_layout(&output);
2023-07-02 18:15:44 -05:00
}
}
2023-07-18 21:10:43 -05:00
Msg::ToggleTag {
output_name,
tag_name,
} => {
tracing::debug!("ToggleTag");
2023-07-18 21:10:43 -05:00
let output = self
.space
.outputs()
.find(|op| op.name() == output_name)
.cloned();
if let Some(output) = output {
output.with_state(|state| {
2023-07-18 21:10:43 -05:00
if let Some(tag) = state.tags.iter_mut().find(|tag| tag.name() == tag_name)
{
tracing::debug!("Setting tag {tag:?} to {}", !tag.active());
tag.set_active(!tag.active());
}
});
self.re_layout(&output);
}
2023-07-02 18:15:44 -05:00
}
2023-07-18 21:10:43 -05:00
Msg::SwitchToTag {
output_name,
tag_name,
} => {
let output = self
.space
.outputs()
.find(|op| op.name() == output_name)
.cloned();
if let Some(output) = output {
output.with_state(|state| {
if !state.tags.iter().any(|tag| tag.name() == tag_name) {
// TODO: notify error
return;
}
for tag in state.tags.iter_mut() {
tag.set_active(false);
}
2023-07-09 17:48:46 -05:00
2023-07-18 21:10:43 -05:00
let Some(tag) = state
.tags
.iter_mut()
.find(|tag| tag.name() == tag_name)
else {
unreachable!()
};
tag.set_active(true);
tracing::debug!(
"focused tags: {:?}",
state
.tags
.iter()
.filter(|tag| tag.active())
.map(|tag| tag.name())
.collect::<Vec<_>>()
);
});
self.re_layout(&output);
}
2023-07-02 18:15:44 -05:00
}
2023-07-09 17:48:46 -05:00
// TODO: add output
2023-07-18 21:10:43 -05:00
Msg::AddTags {
output_name,
tag_names,
} => {
if let Some(output) = self
2023-07-11 11:59:38 -05:00
.space
.outputs()
.find(|output| output.name() == output_name)
{
output.with_state(|state| {
2023-07-18 21:10:43 -05:00
state.tags.extend(tag_names.iter().cloned().map(Tag::new));
tracing::debug!("tags added, are now {:?}", state.tags);
2023-07-09 17:48:46 -05:00
});
}
2023-07-02 18:15:44 -05:00
}
2023-07-18 21:10:43 -05:00
Msg::RemoveTags {
output_name,
tag_names,
} => {
2023-07-11 11:59:38 -05:00
if let Some(output) = self
.space
.outputs()
.find(|output| output.name() == output_name)
{
output.with_state(|state| {
2023-07-18 15:12:23 -05:00
state.tags.retain(|tag| !tag_names.contains(&tag.name()));
2023-07-09 17:48:46 -05:00
});
}
2023-07-02 18:15:44 -05:00
}
2023-07-18 21:10:43 -05:00
Msg::SetLayout {
output_name,
tag_name,
layout,
} => {
let output = self
.space
.outputs()
.find(|op| op.name() == output_name)
.cloned();
2023-07-12 18:50:41 -05:00
if let Some(output) = output {
output.with_state(|state| {
2023-07-18 21:10:43 -05:00
if let Some(tag) = state.tags.iter_mut().find(|tag| tag.name() == tag_name)
{
2023-07-12 18:50:41 -05:00
tag.set_layout(layout);
}
});
self.re_layout(&output);
}
}
2023-07-11 11:59:38 -05:00
Msg::ConnectForAllOutputs { callback_id } => {
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
for output in self.space.outputs() {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::ConnectForAllOutputs {
output_name: output.name(),
}),
},
)
.expect("Send to client failed");
}
self.output_callback_ids.push(callback_id);
}
2023-07-02 18:15:44 -05:00
Msg::Quit => {
self.loop_signal.stop();
}
2023-07-18 10:31:08 -05:00
Msg::Request(request) => {
2023-07-18 21:10:43 -05:00
self.handle_request(request);
}
}
}
fn handle_request(&mut self, request: Request) {
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
match request {
2023-07-19 20:11:15 -05:00
Request::GetWindows => {
2023-07-18 21:10:43 -05:00
let window_ids = self
.windows
.iter()
.map(|win| win.with_state(|state| state.id))
.collect::<Vec<_>>();
// FIXME: figure out what to do if error
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Windows { window_ids },
},
)
.expect("Couldn't send to client");
}
2023-07-19 20:11:15 -05:00
Request::GetWindowProps { window_id } => {
let window = window_id.window(self);
let size = window
.as_ref()
2023-07-18 21:10:43 -05:00
.map(|win| (win.geometry().size.w, win.geometry().size.h));
2023-07-19 20:11:15 -05:00
let loc = window
.as_ref()
.and_then(|win| self.space.element_location(win))
2023-07-18 21:10:43 -05:00
.map(|loc| (loc.x, loc.y));
2023-07-19 20:11:15 -05:00
let (class, title) = window.as_ref().map_or((None, None), |win| {
2023-07-18 21:10:43 -05:00
compositor::with_states(win.toplevel().wl_surface(), |states| {
let lock = states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("failed to acquire lock");
2023-07-19 20:11:15 -05:00
(lock.app_id.clone(), lock.title.clone())
2023-07-18 21:10:43 -05:00
})
});
2023-07-19 20:11:15 -05:00
let floating = window
.as_ref()
2023-07-18 21:10:43 -05:00
.map(|win| win.with_state(|state| state.floating.is_floating()));
2023-07-19 20:11:15 -05:00
let focused = window.as_ref().and_then(|win| {
self.focus_state
.current_focus() // TODO: actual focus
.map(|foc_win| win == &foc_win)
});
2023-07-18 21:10:43 -05:00
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
2023-07-19 20:11:15 -05:00
response: RequestResponse::WindowProps {
size,
loc,
class,
title,
floating,
focused,
},
2023-07-18 21:10:43 -05:00
},
)
.expect("failed to send to client");
}
2023-07-19 18:55:22 -05:00
Request::GetOutputs => {
let output_names = self
2023-07-18 21:10:43 -05:00
.space
.outputs()
.map(|output| output.name())
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
2023-07-19 18:55:22 -05:00
response: RequestResponse::Outputs { output_names },
2023-07-18 21:10:43 -05:00
},
)
.expect("failed to send to client");
}
2023-07-19 18:55:22 -05:00
Request::GetOutputProps { output_name } => {
let output = self
2023-07-18 21:10:43 -05:00
.space
.outputs()
2023-07-19 18:55:22 -05:00
.find(|output| output.name() == output_name);
let res = output.as_ref().and_then(|output| {
output.current_mode().map(|mode| (mode.size.w, mode.size.h))
});
let refresh_rate = output
.as_ref()
.and_then(|output| output.current_mode().map(|mode| mode.refresh));
let model = output
.as_ref()
.map(|output| output.physical_properties().model);
let physical_size = output.as_ref().map(|output| {
(
output.physical_properties().size.w,
output.physical_properties().size.h,
)
});
let make = output
.as_ref()
.map(|output| output.physical_properties().make);
let loc = output
.as_ref()
.map(|output| (output.current_location().x, output.current_location().y));
let focused = self
2023-07-18 21:10:43 -05:00
.focus_state
.focused_output
.as_ref()
2023-07-19 18:55:22 -05:00
.and_then(|foc_op| output.map(|op| op == foc_op));
2023-07-20 15:22:22 -05:00
let tag_ids = output.as_ref().map(|output| {
output.with_state(|state| {
state.tags.iter().map(|tag| tag.id()).collect::<Vec<_>>()
})
});
2023-07-18 21:10:43 -05:00
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
2023-07-19 18:55:22 -05:00
response: RequestResponse::OutputProps {
make,
model,
loc,
res,
refresh_rate,
physical_size,
focused,
2023-07-20 15:22:22 -05:00
tag_ids,
2023-07-19 18:55:22 -05:00
},
2023-07-18 21:10:43 -05:00
},
)
.expect("failed to send to client");
}
2023-07-20 16:05:26 -05:00
Request::GetTags => {
2023-07-19 18:55:22 -05:00
let tag_ids = self
2023-07-18 21:10:43 -05:00
.space
.outputs()
.flat_map(|op| op.with_state(|state| state.tags.clone()))
2023-07-19 18:55:22 -05:00
.map(|tag| tag.id())
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Tags { tag_ids },
},
)
.expect("failed to send to client");
}
2023-07-20 16:05:26 -05:00
Request::GetTagProps { tag_id } => {
let tag = tag_id.tag(self);
let output_name = tag
.as_ref()
2023-07-19 18:55:22 -05:00
.and_then(|tag| tag.output(self))
.map(|output| output.name());
2023-07-20 16:05:26 -05:00
let active = tag.as_ref().map(|tag| tag.active());
let name = tag.as_ref().map(|tag| tag.name());
2023-07-19 18:55:22 -05:00
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
2023-07-20 16:05:26 -05:00
response: RequestResponse::TagProps {
active,
name,
output_name,
},
2023-07-19 18:55:22 -05:00
},
)
.expect("failed to send to client");
2023-07-18 21:10:43 -05:00
}
2023-07-01 19:06:37 -05:00
}
2023-06-09 20:29:17 -05:00
}
pub fn handle_spawn(&self, command: Vec<String>, callback_id: Option<CallbackId>) {
let mut command = command.into_iter();
let Some(program) = command.next() else {
// TODO: notify that command was nothing
return;
};
let program = OsString::from(program);
let Ok(mut child) = async_process::Command::new(&program)
.env("WAYLAND_DISPLAY", self.socket_name.clone())
.stdin(if callback_id.is_some() {
Stdio::piped()
} else {
// piping to null because foot won't open without a callback_id
// otherwise
Stdio::null()
})
.stdout(if callback_id.is_some() {
Stdio::piped()
} else {
Stdio::null()
})
.stderr(if callback_id.is_some() {
Stdio::piped()
} else {
Stdio::null()
})
.args(command)
.spawn()
else {
// TODO: notify user that program doesn't exist
tracing::warn!("tried to run {}, but it doesn't exist", program.to_string_lossy());
return;
};
if let Some(callback_id) = callback_id {
let stdout = child.stdout.take();
let stderr = child.stderr.take();
2023-07-02 18:15:44 -05:00
let stream_out = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist")
.clone();
let stream_err = stream_out.clone();
let stream_exit = stream_out.clone();
if let Some(stdout) = stdout {
let future = async move {
// TODO: use BufReader::new().lines()
let mut reader = futures_lite::io::BufReader::new(stdout);
loop {
let mut buf = String::new();
match reader.read_line(&mut buf).await {
Ok(0) => break,
Ok(_) => {
let mut stream = stream_out.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: Some(buf.trim_end_matches('\n').to_string()),
stderr: None,
exit_code: None,
exit_msg: None,
}),
},
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
Err(err) => {
tracing::warn!("child read err: {err}");
break;
2023-07-02 18:15:44 -05:00
}
}
}
};
// This is not important enough to crash on error, so just print the error instead
if let Err(err) = self.async_scheduler.schedule(future) {
tracing::error!("Failed to schedule future: {err}");
}
}
if let Some(stderr) = stderr {
let future = async move {
let mut reader = futures_lite::io::BufReader::new(stderr);
loop {
let mut buf = String::new();
match reader.read_line(&mut buf).await {
Ok(0) => break,
Ok(_) => {
let mut stream = stream_err.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: None,
stderr: Some(buf.trim_end_matches('\n').to_string()),
exit_code: None,
exit_msg: None,
}),
},
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
Err(err) => {
tracing::warn!("child read err: {err}");
break;
2023-07-02 18:15:44 -05:00
}
}
}
};
if let Err(err) = self.async_scheduler.schedule(future) {
tracing::error!("Failed to schedule future: {err}");
}
}
let future = async move {
match child.status().await {
Ok(exit_status) => {
let mut stream = stream_exit.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: None,
stderr: None,
exit_code: exit_status.code(),
exit_msg: Some(exit_status.to_string()),
}),
},
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
Err(err) => {
tracing::warn!("child wait() err: {err}");
}
}
};
if let Err(err) = self.async_scheduler.schedule(future) {
tracing::error!("Failed to schedule future: {err}");
}
}
}
pub fn re_layout(&mut self, output: &Output) {
2023-07-18 21:10:43 -05:00
let windows = self
.windows
.iter()
.filter(|win| {
win.with_state(|state| {
state
.tags
.iter()
2023-07-19 18:55:22 -05:00
.any(|tag| tag.output(self).is_some_and(|op| &op == output))
2023-07-18 21:10:43 -05:00
})
})
.cloned()
.collect::<Vec<_>>();
let (render, do_not_render) = output.with_state(|state| {
2023-07-11 21:07:51 -05:00
let first_tag = state.focused_tags().next();
if let Some(first_tag) = first_tag {
first_tag.layout().layout(
self.windows.clone(),
state.focused_tags().cloned().collect(),
&self.space,
output,
2023-07-11 21:07:51 -05:00
);
}
windows.iter().cloned().partition::<Vec<_>, _>(|win| {
win.with_state(|win_state| {
2023-07-09 17:48:46 -05:00
if win_state.floating.is_floating() {
return true;
}
for tag in win_state.tags.iter() {
if state.focused_tags().any(|tg| tg == tag) {
2023-07-09 10:00:16 -05:00
return true;
}
}
2023-07-09 10:00:16 -05:00
false
2023-07-09 20:33:25 -05:00
})
})
});
let clone = render.clone();
2023-07-09 20:33:25 -05:00
self.loop_handle.insert_idle(|data| {
schedule_on_commit(data, clone, |dt| {
for win in do_not_render {
dt.state.space.unmap_elem(&win);
}
})
});
2023-07-02 18:15:44 -05:00
// let blocker = WindowBlockerAll::new(render.clone());
// blocker.insert_into_loop(self);
// for win in render.iter() {
// compositor::add_blocker(win.toplevel().wl_surface(), blocker.clone());
// }
// let (blocker, source) = WindowBlocker::block_all::<B>(render.clone());
// for win in render.iter() {
// compositor::add_blocker(win.toplevel().wl_surface(), blocker.clone());
// }
// self.loop_handle.insert_idle(move |data| source(render.clone(), data));
// let (blocker, source) = WindowBlocker::new::<B>(render.clone());
// for win in render.iter() {
// compositor::add_blocker(win.toplevel().wl_surface(), blocker.clone());
// }
// self.loop_handle.insert_idle(move |data| source(render.clone(), render.clone(), data));
}
}
/// Schedule something to be done when windows have finished committing and have become
/// idle.
2023-07-18 21:10:43 -05:00
pub fn schedule_on_commit<F, B: Backend>(
data: &mut CalloopData<B>,
windows: Vec<Window>,
on_commit: F,
) where
F: FnOnce(&mut CalloopData<B>) + 'static,
{
for window in windows.iter() {
2023-07-18 21:10:43 -05:00
if window.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle)) {
data.state.loop_handle.insert_idle(|data| {
schedule_on_commit(data, windows, on_commit);
});
return;
}
2023-07-02 18:15:44 -05:00
}
on_commit(data);
2023-07-02 18:15:44 -05:00
}
impl<B: Backend> State<B> {
2023-07-02 18:15:44 -05:00
pub fn init(
backend_data: B,
2023-07-02 18:15:44 -05:00
display: &mut Display<Self>,
loop_signal: LoopSignal,
loop_handle: LoopHandle<'static, CalloopData<B>>,
2023-07-02 18:15:44 -05:00
) -> Result<Self, Box<dyn Error>> {
let socket = ListeningSocketSource::new_auto()?;
let socket_name = socket.socket_name().to_os_string();
std::env::set_var("WAYLAND_DISPLAY", socket_name.clone());
// Opening a new process will use up a few file descriptors, around 10 for Alacritty, for
// example. Because of this, opening up only around 100 processes would exhaust the file
// descriptor limit on my system (Arch btw) and cause a "Too many open files" crash.
//
// To fix this, I just set the limit to be higher. As Pinnacle is the whole graphical
// environment, I *think* this is ok.
if let Err(err) = smithay::reexports::nix::sys::resource::setrlimit(
smithay::reexports::nix::sys::resource::Resource::RLIMIT_NOFILE,
65536,
65536 * 2,
) {
tracing::error!("Could not raise fd limit: errno {err}");
}
loop_handle.insert_source(socket, |stream, _metadata, data| {
data.display
.handle()
.insert_client(stream, Arc::new(ClientState::default()))
.expect("Could not insert client into loop handle");
})?;
loop_handle.insert_source(
Generic::new(
display.backend().poll_fd().as_raw_fd(),
Interest::READ,
Mode::Level,
),
|_readiness, _metadata, data| {
data.display.dispatch_clients(&mut data.state)?;
Ok(PostAction::Continue)
},
)?;
let (tx_channel, rx_channel) = calloop::channel::channel::<Msg>();
// We want to replace the client if a new one pops up
// TODO: there should only ever be one client working at a time, and creating a new client
// | when one is already running should be impossible.
// INFO: this source try_clone()s the stream
loop_handle.insert_source(PinnacleSocketSource::new(tx_channel)?, |stream, _, data| {
if let Some(old_stream) = data
.state
.api_state
.stream
.replace(Arc::new(Mutex::new(stream)))
{
old_stream
.lock()
.expect("Couldn't lock old stream")
.shutdown(std::net::Shutdown::Both)
.expect("Couldn't shutdown old stream");
}
})?;
let (executor, sched) =
calloop::futures::executor::<()>().expect("Couldn't create executor");
loop_handle.insert_source(executor, |_, _, _| {})?;
// TODO: move all this into the lua api
let config_path = std::env::var("PINNACLE_CONFIG").unwrap_or_else(|_| {
let mut default_path =
std::env::var("XDG_CONFIG_HOME").unwrap_or("~/.config".to_string());
default_path.push_str("/pinnacle/init.lua");
default_path
});
if Path::new(&config_path).exists() {
let lua_path = std::env::var("LUA_PATH").expect("Lua is not installed!");
let mut local_lua_path = std::env::current_dir()
.expect("Couldn't get current dir")
.to_string_lossy()
.to_string();
local_lua_path.push_str("/api/lua"); // TODO: get from crate root and do dynamically
let new_lua_path =
format!("{local_lua_path}/?.lua;{local_lua_path}/?/init.lua;{local_lua_path}/lib/?.lua;{local_lua_path}/lib/?/init.lua;{lua_path}");
let lua_cpath = std::env::var("LUA_CPATH").expect("Lua is not installed!");
let new_lua_cpath = format!("{local_lua_path}/lib/?.so;{lua_cpath}");
std::process::Command::new("lua5.4")
.arg(config_path)
.env("LUA_PATH", new_lua_path)
.env("LUA_CPATH", new_lua_cpath)
.spawn()
.expect("Could not start config process");
} else {
tracing::error!("Could not find {}", config_path);
}
let display_handle = display.handle();
let mut seat_state = SeatState::new();
let mut seat = seat_state.new_wl_seat(&display_handle, backend_data.seat_name());
seat.add_pointer();
seat.add_keyboard(XkbConfig::default(), 200, 25)?;
loop_handle.insert_idle(|data| {
data.state
.loop_handle
.insert_source(rx_channel, |msg, _, data| match msg {
Event::Msg(msg) => data.state.handle_msg(msg),
Event::Closed => todo!(),
})
2023-07-02 18:15:44 -05:00
.unwrap(); // TODO: unwrap
});
2023-07-02 18:15:44 -05:00
Ok(Self {
backend_data,
loop_signal,
loop_handle,
clock: Clock::<Monotonic>::new()?,
compositor_state: CompositorState::new::<Self>(&display_handle),
data_device_state: DataDeviceState::new::<Self>(&display_handle),
seat_state,
pointer_location: (0.0, 0.0).into(),
shm_state: ShmState::new::<Self>(&display_handle, vec![]),
space: Space::<Window>::default(),
cursor_status: CursorImageStatus::Default,
output_manager_state: OutputManagerState::new_with_xdg_output::<Self>(&display_handle),
xdg_shell_state: XdgShellState::new::<Self>(&display_handle),
viewporter_state: ViewporterState::new::<Self>(&display_handle),
fractional_scale_manager_state: FractionalScaleManagerState::new::<Self>(
&display_handle,
),
input_state: InputState::new(),
api_state: ApiState::new(),
focus_state: FocusState::new(),
2023-07-02 18:15:44 -05:00
seat,
move_mode: false,
socket_name: socket_name.to_string_lossy().to_string(),
popup_manager: PopupManager::default(),
async_scheduler: sched,
windows: vec![],
2023-07-11 11:59:38 -05:00
output_callback_ids: vec![],
2023-07-02 18:15:44 -05:00
})
}
2023-06-05 21:08:37 -05:00
}
2023-06-09 20:29:17 -05:00
pub struct CalloopData<B: Backend> {
pub display: Display<State<B>>,
pub state: State<B>,
2023-06-02 16:01:48 -05:00
}
#[derive(Default)]
pub struct ClientState {
pub compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
}
2023-06-09 20:29:17 -05:00
#[derive(Debug, Copy, Clone)]
pub struct SurfaceDmabufFeedback<'a> {
pub render_feedback: &'a DmabufFeedback,
pub scanout_feedback: &'a DmabufFeedback,
}
2023-06-21 19:08:29 -05:00
// TODO: docs
2023-06-09 20:29:17 -05:00
pub fn take_presentation_feedback(
output: &Output,
space: &Space<Window>,
render_element_states: &RenderElementStates,
) -> OutputPresentationFeedback {
let mut output_presentation_feedback = OutputPresentationFeedback::new(output);
space.elements().for_each(|window| {
if space.outputs_for_element(window).contains(output) {
window.take_presentation_feedback(
&mut output_presentation_feedback,
surface_primary_scanout_output,
|surface, _| {
surface_presentation_feedback_flags_from_states(surface, render_element_states)
},
);
}
});
// let map = smithay::desktop::layer_map_for_output(output);
// for layer_surface in map.layers() {
// layer_surface.take_presentation_feedback(
// &mut output_presentation_feedback,
// surface_primary_scanout_output,
// |surface, _| {
// surface_presentation_feedback_flags_from_states(surface, render_element_states)
// },
// );
// }
2023-06-09 20:29:17 -05:00
output_presentation_feedback
}
2023-06-17 18:55:04 -05:00
2023-06-21 19:08:29 -05:00
/// State containing the config API's stream.
2023-06-17 21:02:58 -05:00
#[derive(Default)]
2023-06-17 18:55:04 -05:00
pub struct ApiState {
2023-07-11 11:59:38 -05:00
// TODO: this may not need to be in an arc mutex because of the move to async
2023-06-21 14:48:38 -05:00
pub stream: Option<Arc<Mutex<UnixStream>>>,
2023-06-17 18:55:04 -05:00
}
2023-06-17 21:02:58 -05:00
impl ApiState {
pub fn new() -> Self {
Default::default()
}
}
pub trait WithState {
type State: Default;
fn with_state<F, T>(&self, func: F) -> T
where
F: FnMut(&mut Self::State) -> T;
}
#[derive(Default, Debug)]
pub struct WlSurfaceState {
pub resize_state: ResizeSurfaceState,
}
impl WithState for WlSurface {
type State = WlSurfaceState;
fn with_state<F, T>(&self, mut func: F) -> T
where
F: FnMut(&mut Self::State) -> T,
{
compositor::with_states(self, |states| {
states
.data_map
.insert_if_missing(RefCell::<Self::State>::default);
let state = states
.data_map
.get::<RefCell<Self::State>>()
.expect("This should never happen");
func(&mut state.borrow_mut())
})
}
}