Completely rip out the old msgpack stuff

Did this break anything? ¯\_(ツ)_/¯
This commit is contained in:
Ottatop 2024-01-21 23:42:48 -06:00
parent 9acd0e5ce3
commit 0b88ad298b
12 changed files with 1911 additions and 3594 deletions

30
Cargo.lock generated
View file

@ -1489,12 +1489,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -1556,8 +1550,6 @@ dependencies = [
"pinnacle-api-defs", "pinnacle-api-defs",
"prost", "prost",
"prost-types", "prost-types",
"rmp",
"rmp-serde",
"serde", "serde",
"shellexpand", "shellexpand",
"smithay", "smithay",
@ -1868,28 +1860,6 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rmp"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"

View file

@ -19,8 +19,6 @@ thiserror = "1"
xcursor = { version = "0.3", optional = true } xcursor = { version = "0.3", optional = true }
image = { version = "0.24", default-features = false, optional = true } image = { version = "0.24", default-features = false, optional = true }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
rmp = { version = "0.8.12" }
rmp-serde = { version = "1.1.2" }
x11rb = { version = "0.13", default-features = false, features = ["composite"], optional = true } x11rb = { version = "0.13", default-features = false, features = ["composite"], optional = true }
shellexpand = "3.1.0" shellexpand = "3.1.0"
toml = "0.8" toml = "0.8"

2034
src/api.rs

File diff suppressed because it is too large Load diff

View file

@ -1,824 +0,0 @@
use std::{ffi::OsString, process::Stdio};
use smithay::{
desktop::space::SpaceElement,
input::keyboard::XkbConfig,
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
utils::{Point, Rectangle, SERIAL_COUNTER},
wayland::{compositor, shell::xdg::XdgToplevelSurfaceData},
};
use sysinfo::ProcessRefreshKind;
use tokio::io::AsyncBufReadExt;
use crate::{
api::msg::{
Args, CallbackId, KeyIntOrString, Msg, OutgoingMsg, Request, RequestId, RequestResponse,
},
config::ConnectorSavedState,
focus::FocusTarget,
tag::Tag,
window::WindowElement,
};
use crate::state::{State, WithState};
impl State {
/// Handle a client message.
pub fn handle_msg(&mut self, msg: Msg) {
tracing::trace!("Got {msg:?}");
match msg {
Msg::SetKeybind {
key,
modifiers,
callback_id,
} => {
let key = match key {
KeyIntOrString::Int(num) => {
tracing::info!("set keybind: {:?}, raw {}", modifiers, num);
num
}
KeyIntOrString::String(s) => {
if s.chars().count() == 1 {
let Some(ch) = s.chars().next() else { unreachable!() };
let raw = xkbcommon::xkb::Keysym::from_char(ch).raw();
tracing::info!("set keybind: {:?}, {:?} (raw {})", modifiers, ch, raw);
raw
} else {
let raw = xkbcommon::xkb::keysym_from_name(
&s,
xkbcommon::xkb::KEYSYM_NO_FLAGS,
)
.raw();
tracing::info!("set keybind: {:?}, {:?}", modifiers, raw);
raw
}
}
};
self.input_state
.keybinds
.insert((modifiers.into(), key.into()), callback_id);
}
Msg::SetMousebind {
modifiers,
button,
edge,
callback_id,
} => {
// TODO: maybe validate/parse valid codes?
self.input_state
.mousebinds
.insert((modifiers.into(), button, edge), callback_id);
}
Msg::CloseWindow { window_id } => {
if let Some(window) = window_id.window(self) {
match window {
WindowElement::Wayland(window) => window.toplevel().send_close(),
WindowElement::X11(surface) => {
surface.close().expect("failed to close x11 win");
}
WindowElement::X11OverrideRedirect(_) => (),
_ => unreachable!(),
}
}
}
Msg::Spawn {
command,
callback_id,
} => {
self.handle_spawn(command, callback_id);
}
Msg::SpawnOnce {
command,
callback_id,
} => {
self.system_processes
.refresh_processes_specifics(ProcessRefreshKind::new());
let Some(arg0) = command.first() else {
tracing::warn!("No command specified for `SpawnOnce`");
return;
};
let compositor_pid = std::process::id();
let already_running =
self.system_processes
.processes_by_exact_name(arg0)
.any(|proc| {
proc.parent()
.is_some_and(|parent_pid| parent_pid.as_u32() == compositor_pid)
});
if !already_running {
self.handle_spawn(command, callback_id);
}
}
Msg::SetEnv { key, value } => std::env::set_var(key, value),
Msg::SetWindowSize {
window_id,
width,
height,
} => {
let Some(window) = window_id.window(self) else { return };
// TODO: tiled vs floating
// FIXME: this will map unmapped windows at 0,0
let window_loc = self
.space
.element_location(&window)
.unwrap_or((0, 0).into());
let mut window_size = window.geometry().size;
if let Some(width) = width {
window_size.w = width;
}
if let Some(height) = height {
window_size.h = height;
}
use crate::window::window_state::FloatingOrTiled;
let rect = Rectangle::from_loc_and_size(window_loc, window_size);
window.change_geometry(rect);
window.with_state(|state| {
state.floating_or_tiled = match state.floating_or_tiled {
FloatingOrTiled::Floating(_) => FloatingOrTiled::Floating(rect),
FloatingOrTiled::Tiled(_) => FloatingOrTiled::Tiled(Some(rect)),
}
});
for output in self.space.outputs_for_element(&window) {
self.update_windows(&output);
self.schedule_render(&output);
}
}
Msg::MoveWindowToTag { window_id, tag_id } => {
let Some(window) = window_id.window(self) else { return };
let Some(tag) = tag_id.tag(self) else { return };
window.with_state(|state| {
state.tags = vec![tag.clone()];
});
let Some(output) = tag.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::ToggleTagOnWindow { window_id, tag_id } => {
let Some(window) = window_id.window(self) else { return };
let Some(tag) = tag_id.tag(self) else { return };
window.with_state(|state| {
if state.tags.contains(&tag) {
state.tags.retain(|tg| tg != &tag);
} else {
state.tags.push(tag.clone());
}
});
let Some(output) = tag.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::ToggleFloating { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.toggle_floating();
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::ToggleFullscreen { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.toggle_fullscreen();
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::ToggleMaximized { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.toggle_maximized();
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::AddWindowRule { cond, rule } => {
self.config.window_rules.push((cond, rule));
}
Msg::WindowMoveGrab { button } => {
let Some((FocusTarget::Window(window), _)) =
self.focus_target_under(self.pointer_location)
else {
return;
};
let Some(wl_surf) = window.wl_surface() else { return };
let seat = self.seat.clone();
// We use the server one and not the client because windows like Steam don't provide
// GrabStartData, so we need to create it ourselves.
crate::grab::move_grab::move_request_server(
self,
&wl_surf,
&seat,
SERIAL_COUNTER.next_serial(),
button,
);
}
Msg::WindowResizeGrab { button } => {
// TODO: in the future, there may be movable layer surfaces
let pointer_loc = self.pointer_location;
let Some((FocusTarget::Window(window), window_loc)) =
self.focus_target_under(pointer_loc)
else {
return;
};
let Some(wl_surf) = window.wl_surface() else { return };
let window_geometry = window.geometry();
let window_x = window_loc.x as f64;
let window_y = window_loc.y as f64;
let window_width = window_geometry.size.w as f64;
let window_height = window_geometry.size.h as f64;
let half_width = window_x + window_width / 2.0;
let half_height = window_y + window_height / 2.0;
let full_width = window_x + window_width;
let full_height = window_y + window_height;
let edges = match pointer_loc {
Point { x, y, .. }
if (window_x..=half_width).contains(&x)
&& (window_y..=half_height).contains(&y) =>
{
ResizeEdge::TopLeft
}
Point { x, y, .. }
if (half_width..=full_width).contains(&x)
&& (window_y..=half_height).contains(&y) =>
{
ResizeEdge::TopRight
}
Point { x, y, .. }
if (window_x..=half_width).contains(&x)
&& (half_height..=full_height).contains(&y) =>
{
ResizeEdge::BottomLeft
}
Point { x, y, .. }
if (half_width..=full_width).contains(&x)
&& (half_height..=full_height).contains(&y) =>
{
ResizeEdge::BottomRight
}
_ => ResizeEdge::None,
};
crate::grab::resize_grab::resize_request_server(
self,
&wl_surf,
&self.seat.clone(),
SERIAL_COUNTER.next_serial(),
edges.into(),
button,
);
}
// Tags ----------------------------------------
Msg::ToggleTag { tag_id } => {
tracing::debug!("ToggleTag");
if let Some(tag) = tag_id.tag(self) {
tag.set_active(!tag.active());
if let Some(output) = tag.output(self) {
self.update_windows(&output);
self.update_focus(&output);
self.schedule_render(&output);
}
}
}
Msg::SwitchToTag { tag_id } => {
let Some(tag) = tag_id.tag(self) else { return };
let Some(output) = tag.output(self) else { return };
output.with_state(|state| {
for op_tag in state.tags.iter_mut() {
op_tag.set_active(false);
}
tag.set_active(true);
});
self.update_windows(&output);
self.update_focus(&output);
self.schedule_render(&output);
}
Msg::AddTags {
output_name,
tag_names,
} => {
let new_tags = tag_names.into_iter().map(Tag::new).collect::<Vec<_>>();
if let Some(saved_state) = self.config.connector_saved_states.get_mut(&output_name)
{
let mut tags = saved_state.tags.clone();
tags.extend(new_tags.clone());
saved_state.tags = tags;
} else {
self.config.connector_saved_states.insert(
output_name.clone(),
ConnectorSavedState {
tags: new_tags.clone(),
..Default::default()
},
);
}
if let Some(output) = self
.space
.outputs()
.find(|output| output.name() == output_name.0)
{
output.with_state(|state| {
state.tags.extend(new_tags.clone());
tracing::debug!("tags added, are now {:?}", state.tags);
});
// replace tags that windows have that are the same id
// (this should only happen on config reload)
for tag in new_tags {
for window in self.windows.iter() {
window.with_state(|state| {
for win_tag in state.tags.iter_mut() {
if win_tag.id() == tag.id() {
*win_tag = tag.clone();
}
}
});
}
}
}
}
Msg::RemoveTags { tag_ids } => {
let tags = tag_ids
.into_iter()
.filter_map(|tag_id| tag_id.tag(self))
.collect::<Vec<_>>();
for tag in tags {
for saved_state in self.config.connector_saved_states.values_mut() {
saved_state.tags.retain(|tg| tg != &tag);
}
let Some(output) = tag.output(self) else { continue };
output.with_state(|state| {
state.tags.retain(|tg| tg != &tag);
});
}
}
Msg::SetLayout { tag_id, layout } => {
let Some(tag) = tag_id.tag(self) else { return };
tag.set_layout(layout);
let Some(output) = tag.output(self) else { return };
self.update_windows(&output);
self.schedule_render(&output);
}
Msg::ConnectForAllOutputs { callback_id } => {
let stream = self
.api_state
.stream
.as_ref()
.expect("stream doesn't exist");
for output in self.space.outputs() {
crate::api::send_to_client(
&mut stream.lock().expect("couldn't lock stream"),
&OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::ConnectForAllOutputs {
output_name: output.name(),
}),
},
)
.expect("Send to client failed");
}
self.config.output_callback_ids.push(callback_id);
}
Msg::SetOutputLocation { output_name, x, y } => {
if let Some(saved_state) = self.config.connector_saved_states.get_mut(&output_name)
{
if let Some(x) = x {
saved_state.loc.x = x;
}
if let Some(y) = y {
saved_state.loc.y = y;
}
} else {
self.config.connector_saved_states.insert(
output_name.clone(),
ConnectorSavedState {
loc: (x.unwrap_or_default(), y.unwrap_or_default()).into(),
..Default::default()
},
);
}
let Some(output) = output_name.output(self) else { return };
let mut loc = output.current_location();
if let Some(x) = x {
loc.x = x;
}
if let Some(y) = y {
loc.y = y;
}
output.change_current_state(None, None, None, Some(loc));
self.space.map_output(&output, loc);
tracing::debug!("Mapping output {} to {loc:?}", output.name());
self.update_windows(&output);
}
Msg::Quit => {
tracing::info!("Quitting Pinnacle");
self.shutdown();
}
Msg::SetXkbConfig {
rules,
variant,
layout,
model,
options,
} => {
let new_config = XkbConfig {
rules: &rules.unwrap_or_default(),
model: &model.unwrap_or_default(),
layout: &layout.unwrap_or_default(),
variant: &variant.unwrap_or_default(),
options,
};
if let Some(kb) = self.seat.get_keyboard() {
if let Err(err) = kb.set_xkb_config(self, new_config) {
tracing::error!("Failed to set xkbconfig: {err}");
}
}
}
Msg::SetLibinputSetting(setting) => {
for device in self.input_state.libinput_devices.iter_mut() {
// We're just gonna indiscriminately apply everything and ignore errors
setting.apply_to_device(device);
}
self.input_state.libinput_settings.push(setting);
}
Msg::Request {
request_id,
request,
} => {
self.handle_request(request_id, request);
}
}
}
/// Handle a client request.
fn handle_request(&mut self, request_id: RequestId, request: Request) {
let stream = self
.api_state
.stream
.clone() // clone due to use of self below
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
match request {
Request::GetWindows => {
let window_ids = self
.windows
.iter()
.map(|win| win.with_state(|state| state.id))
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Windows { window_ids },
},
)
.expect("Couldn't send to client");
}
Request::GetWindowProps { window_id } => {
let window = window_id.window(self);
let size = window
.as_ref()
.map(|win| (win.geometry().size.w, win.geometry().size.h));
let loc = window
.as_ref()
.and_then(|win| self.space.element_location(win))
.map(|loc| (loc.x, loc.y));
let (class, title) = window.as_ref().map_or((None, None), |win| match &win {
WindowElement::Wayland(_) => {
if let Some(wl_surf) = win.wl_surface() {
compositor::with_states(&wl_surf, |states| {
let lock = states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("failed to acquire lock");
(lock.app_id.clone(), lock.title.clone())
})
} else {
(None, None)
}
}
WindowElement::X11(surface) | WindowElement::X11OverrideRedirect(surface) => {
(Some(surface.class()), Some(surface.title()))
}
_ => unreachable!(),
});
let focused = window.as_ref().and_then(|win| {
let output = win.output(self)?;
self.focused_window(&output).map(|foc_win| win == &foc_win)
});
let floating = window
.as_ref()
.map(|win| win.with_state(|state| state.floating_or_tiled.is_floating()));
let fullscreen_or_maximized = window
.as_ref()
.map(|win| win.with_state(|state| state.fullscreen_or_maximized));
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::WindowProps {
size,
loc,
class,
title,
focused,
floating,
fullscreen_or_maximized,
},
},
)
.expect("failed to send to client");
}
Request::GetOutputs => {
let output_names = self
.space
.outputs()
.map(|output| output.name())
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Outputs { output_names },
},
)
.expect("failed to send to client");
}
Request::GetOutputProps { output_name } => {
let output = self
.space
.outputs()
.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
.focus_state
.focused_output
.as_ref()
.and_then(|foc_op| output.map(|op| op == foc_op));
let tag_ids = output.as_ref().map(|output| {
output.with_state(|state| {
state.tags.iter().map(|tag| tag.id()).collect::<Vec<_>>()
})
});
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::OutputProps {
make,
model,
loc,
res,
refresh_rate,
physical_size,
focused,
tag_ids,
},
},
)
.expect("failed to send to client");
}
Request::GetTags => {
let tag_ids = self
.space
.outputs()
.flat_map(|op| op.with_state(|state| state.tags.clone()))
.map(|tag| tag.id())
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Tags { tag_ids },
},
)
.expect("failed to send to client");
}
Request::GetTagProps { tag_id } => {
let tag = tag_id.tag(self);
let output_name = tag
.as_ref()
.and_then(|tag| tag.output(self))
.map(|output| output.name());
let active = tag.as_ref().map(|tag| tag.active());
let name = tag.as_ref().map(|tag| tag.name());
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::TagProps {
active,
name,
output_name,
},
},
)
.expect("failed to send to client");
}
}
}
// Welcome to indentation hell
/// Handle a received spawn command by spawning the command and hooking up any callbacks.
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
tracing::warn!("got an empty command");
return;
};
let program = OsString::from(program);
let Ok(mut child) = tokio::process::Command::new(&program)
.envs(
[("WAYLAND_DISPLAY", self.socket_name.clone())]
.into_iter()
.chain(self.xdisplay.map(|xdisp| ("DISPLAY", format!(":{xdisp}")))),
)
.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();
let stream_out = self.api_state.stream.clone().expect("Stream doesn't exist");
let stream_err = stream_out.clone();
let stream_exit = stream_out.clone();
if let Some(stdout) = stdout {
let future = async move {
let mut reader = tokio::io::BufReader::new(stdout).lines();
while let Ok(Some(line)) = reader.next_line().await {
let msg = OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: Some(line),
stderr: None,
exit_code: None,
exit_msg: None,
}),
};
crate::api::send_to_client(
&mut stream_out.lock().expect("Couldn't lock stream"),
&msg,
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
};
tokio::spawn(future);
}
if let Some(stderr) = stderr {
let future = async move {
let mut reader = tokio::io::BufReader::new(stderr).lines();
while let Ok(Some(line)) = reader.next_line().await {
let msg = OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: None,
stderr: Some(line),
exit_code: None,
exit_msg: None,
}),
};
crate::api::send_to_client(
&mut stream_err.lock().expect("Couldn't lock stream"),
&msg,
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
};
tokio::spawn(future);
}
let future = async move {
match child.wait().await {
Ok(exit_status) => {
let msg = OutgoingMsg::CallCallback {
callback_id,
args: Some(Args::Spawn {
stdout: None,
stderr: None,
exit_code: exit_status.code(),
exit_msg: Some(exit_status.to_string()),
}),
};
crate::api::send_to_client(
&mut stream_exit.lock().expect("Couldn't lock stream"),
&msg,
)
.expect("Send to client failed"); // TODO: notify instead of crash
}
Err(err) => {
tracing::warn!("child wait() err: {err}");
}
}
};
tokio::spawn(future);
}
}
}

View file

@ -1,335 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// The MessagePack format for these is a one-element map where the element's key is the enum name and its
// value is a map of the enum's values
use smithay::input::keyboard::ModifiersState;
use crate::{
input::libinput::LibinputSetting,
layout::Layout,
output::OutputName,
tag::TagId,
window::{
rules::{WindowRule, WindowRuleCondition},
window_state::{FullscreenOrMaximized, WindowId},
},
};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32);
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
pub enum KeyIntOrString {
Int(u32),
String(String),
}
#[derive(Debug, Hash, serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum MouseEdge {
Press,
Release,
}
#[derive(Debug, serde::Deserialize)]
pub enum Msg {
// Input
SetKeybind {
key: KeyIntOrString,
modifiers: Vec<Modifier>,
callback_id: CallbackId,
},
SetMousebind {
modifiers: Vec<Modifier>,
button: u32,
edge: MouseEdge,
callback_id: CallbackId,
},
// Window management
CloseWindow {
window_id: WindowId,
},
SetWindowSize {
window_id: WindowId,
#[serde(default)]
width: Option<i32>,
#[serde(default)]
height: Option<i32>,
},
MoveWindowToTag {
window_id: WindowId,
tag_id: TagId,
},
ToggleTagOnWindow {
window_id: WindowId,
tag_id: TagId,
},
ToggleFloating {
window_id: WindowId,
},
ToggleFullscreen {
window_id: WindowId,
},
ToggleMaximized {
window_id: WindowId,
},
AddWindowRule {
cond: WindowRuleCondition,
rule: WindowRule,
},
WindowMoveGrab {
button: u32,
},
WindowResizeGrab {
button: u32,
},
// Tag management
ToggleTag {
tag_id: TagId,
},
SwitchToTag {
tag_id: TagId,
},
AddTags {
/// The name of the output you want these tags on.
output_name: OutputName,
tag_names: Vec<String>,
},
RemoveTags {
/// The name of the output you want these tags removed from.
tag_ids: Vec<TagId>,
},
SetLayout {
tag_id: TagId,
layout: Layout,
},
// Output management
ConnectForAllOutputs {
callback_id: CallbackId,
},
SetOutputLocation {
output_name: OutputName,
#[serde(default)]
x: Option<i32>,
#[serde(default)]
y: Option<i32>,
},
// Process management
/// Spawn a program with an optional callback.
Spawn {
command: Vec<String>,
#[serde(default)]
callback_id: Option<CallbackId>,
},
SpawnOnce {
command: Vec<String>,
#[serde(default)]
callback_id: Option<CallbackId>,
},
SetEnv {
key: String,
value: String,
},
// Pinnacle management
/// Quit the compositor.
Quit,
// Input management
SetXkbConfig {
#[serde(default)]
rules: Option<String>,
#[serde(default)]
variant: Option<String>,
#[serde(default)]
layout: Option<String>,
#[serde(default)]
model: Option<String>,
#[serde(default)]
options: Option<String>,
},
SetLibinputSetting(LibinputSetting),
Request {
request_id: RequestId,
request: Request,
},
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RequestId(u32);
#[allow(clippy::enum_variant_names)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
/// Messages that require a server response, usually to provide some data.
pub enum Request {
// Windows
GetWindows,
GetWindowProps { window_id: WindowId },
// Outputs
GetOutputs,
GetOutputProps { output_name: String },
// Tags
GetTags,
GetTagProps { tag_id: TagId },
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
pub enum Modifier {
Shift = 0b0000_0001,
Ctrl = 0b0000_0010,
Alt = 0b0000_0100,
Super = 0b0000_1000,
}
/// A bitmask of [`Modifier`]s for the purpose of hashing.
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub struct ModifierMask(u8);
impl From<Vec<Modifier>> for ModifierMask {
fn from(value: Vec<Modifier>) -> Self {
let value = value.into_iter();
let mut mask: u8 = 0b0000_0000;
for modifier in value {
mask |= modifier as u8;
}
Self(mask)
}
}
impl From<&[Modifier]> for ModifierMask {
fn from(value: &[Modifier]) -> Self {
let value = value.iter();
let mut mask: u8 = 0b0000_0000;
for modifier in value {
mask |= *modifier as u8;
}
Self(mask)
}
}
impl From<ModifiersState> for ModifierMask {
fn from(state: ModifiersState) -> Self {
let mut mask: u8 = 0b0000_0000;
if state.shift {
mask |= Modifier::Shift as u8;
}
if state.ctrl {
mask |= Modifier::Ctrl as u8;
}
if state.alt {
mask |= Modifier::Alt as u8;
}
if state.logo {
mask |= Modifier::Super as u8;
}
Self(mask)
}
}
impl ModifierMask {
#[allow(dead_code)]
pub fn values(self) -> Vec<Modifier> {
let mut res = Vec::<Modifier>::new();
if self.0 & Modifier::Shift as u8 == Modifier::Shift as u8 {
res.push(Modifier::Shift);
}
if self.0 & Modifier::Ctrl as u8 == Modifier::Ctrl as u8 {
res.push(Modifier::Ctrl);
}
if self.0 & Modifier::Alt as u8 == Modifier::Alt as u8 {
res.push(Modifier::Alt);
}
if self.0 & Modifier::Super as u8 == Modifier::Super as u8 {
res.push(Modifier::Super);
}
res
}
}
/// Messages sent from the server to the client.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum OutgoingMsg {
CallCallback {
callback_id: CallbackId,
#[serde(default)]
args: Option<Args>,
},
RequestResponse {
request_id: RequestId,
response: RequestResponse,
},
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Args {
/// Send a message with lines from the spawned process.
Spawn {
#[serde(default)]
stdout: Option<String>,
#[serde(default)]
stderr: Option<String>,
#[serde(default)]
exit_code: Option<i32>,
#[serde(default)]
exit_msg: Option<String>,
},
ConnectForAllOutputs {
output_name: String,
},
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum RequestResponse {
Window {
window_id: Option<WindowId>,
},
Windows {
window_ids: Vec<WindowId>,
},
WindowProps {
size: Option<(i32, i32)>,
loc: Option<(i32, i32)>,
class: Option<String>,
title: Option<String>,
focused: Option<bool>,
floating: Option<bool>,
fullscreen_or_maximized: Option<FullscreenOrMaximized>,
},
Output {
output_name: Option<String>,
},
Outputs {
output_names: Vec<String>,
},
OutputProps {
/// The make of the output.
make: Option<String>,
/// The model of the output.
model: Option<String>,
/// The location of the output in the space.
loc: Option<(i32, i32)>,
/// The resolution of the output.
res: Option<(i32, i32)>,
/// The refresh rate of the output.
refresh_rate: Option<i32>,
/// The size of the output, in millimeters.
physical_size: Option<(i32, i32)>,
/// Whether the output is focused or not.
focused: Option<bool>,
tag_ids: Option<Vec<TagId>>,
},
Tags {
tag_ids: Vec<TagId>,
},
TagProps {
active: Option<bool>,
name: Option<String>,
output_name: Option<String>,
},
}

File diff suppressed because it is too large Load diff

View file

@ -72,7 +72,6 @@ use smithay_drm_extras::{
}; };
use crate::{ use crate::{
api::msg::{Args, OutgoingMsg},
backend::Backend, backend::Backend,
config::ConnectorSavedState, config::ConnectorSavedState,
output::OutputName, output::OutputName,
@ -985,36 +984,11 @@ impl State {
output.with_state(|state| state.tags = tags.clone()); output.with_state(|state| state.tags = tags.clone());
} else { } else {
// Run any output callbacks // Run any output callbacks
let clone = output.clone(); for sender in self.config.output_callback_senders.iter() {
self.schedule( let _ = sender.send(Ok(ConnectForAllResponse {
|dt| dt.state.api_state.stream.is_some(), output_name: Some(output.name()),
move |dt| {
let stream = dt
.state
.api_state
.stream
.as_ref()
.expect("stream doesn't exist");
let mut stream = stream.lock().expect("couldn't lock stream");
for callback_id in dt.state.config.output_callback_ids.iter() {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::CallCallback {
callback_id: *callback_id,
args: Some(Args::ConnectForAllOutputs {
output_name: clone.name(),
}),
},
)
.expect("Send to client failed");
}
for grpc_sender in dt.state.config.grpc_output_callback_senders.iter() {
let _ = grpc_sender.send(Ok(ConnectForAllResponse {
output_name: Some(clone.name()),
})); }));
} }
},
);
} }
} }

View file

@ -1,11 +1,8 @@
use crate::{ use crate::{
api::{ api::{
msg::ModifierMask,
protocol::{
InputService, OutputService, PinnacleService, ProcessService, TagService, WindowService, InputService, OutputService, PinnacleService, ProcessService, TagService, WindowService,
}, },
PinnacleSocketSource, input::ModifierMask,
},
output::OutputName, output::OutputName,
tag::Tag, tag::Tag,
window::rules::{WindowRule, WindowRuleCondition}, window::rules::{WindowRule, WindowRuleCondition},
@ -14,7 +11,6 @@ use std::{
collections::HashMap, collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Stdio, process::Stdio,
sync::{Arc, Mutex},
}; };
use anyhow::Context; use anyhow::Context;
@ -32,9 +28,9 @@ use smithay::{
utils::{Logical, Point}, utils::{Logical, Point},
}; };
use sysinfo::ProcessRefreshKind; use sysinfo::ProcessRefreshKind;
use tokio::sync::mpsc::UnboundedSender;
use toml::Table; use toml::Table;
use crate::api::msg::{CallbackId, Modifier};
use xkbcommon::xkb::Keysym; use xkbcommon::xkb::Keysym;
use crate::{ use crate::{
@ -57,8 +53,34 @@ pub struct Metaconfig {
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]
pub struct Keybind { pub struct Keybind {
pub modifiers: Vec<Modifier>, modifiers: Vec<Modifier>,
pub key: Key, key: Key,
}
#[derive(serde::Deserialize, Debug, Clone, Copy)]
enum Modifier {
Shift,
Ctrl,
Alt,
Super,
}
// TODO: refactor metaconfig input
impl From<Vec<self::Modifier>> for ModifierMask {
fn from(mods: Vec<self::Modifier>) -> Self {
let mut mask = ModifierMask::empty();
for m in mods {
match m {
Modifier::Shift => mask |= ModifierMask::SHIFT,
Modifier::Ctrl => mask |= ModifierMask::CTRL,
Modifier::Alt => mask |= ModifierMask::ALT,
Modifier::Super => mask |= ModifierMask::SUPER,
}
}
mask
}
} }
// TODO: accept xkbcommon names instead // TODO: accept xkbcommon names instead
@ -141,10 +163,7 @@ pub enum Key {
pub struct Config { pub struct Config {
/// Window rules and conditions on when those rules should apply /// Window rules and conditions on when those rules should apply
pub window_rules: Vec<(WindowRuleCondition, WindowRule)>, pub window_rules: Vec<(WindowRuleCondition, WindowRule)>,
/// All callbacks that should be run when outputs are connected pub output_callback_senders: Vec<UnboundedSender<Result<ConnectForAllResponse, tonic::Status>>>,
pub output_callback_ids: Vec<CallbackId>,
pub grpc_output_callback_senders:
Vec<tokio::sync::mpsc::UnboundedSender<Result<ConnectForAllResponse, tonic::Status>>>,
/// Saved states when outputs are disconnected /// Saved states when outputs are disconnected
pub connector_saved_states: HashMap<OutputName, ConnectorSavedState>, pub connector_saved_states: HashMap<OutputName, ConnectorSavedState>,
} }
@ -214,13 +233,6 @@ impl State {
config_join_handle.abort(); config_join_handle.abort();
} }
if let Some(token) = self.api_state.socket_token {
// Should only happen if parsing the metaconfig failed
self.loop_handle.remove(token);
}
let tx_channel = self.api_state.tx_channel.clone();
// Love that trailing slash // Love that trailing slash
let data_home = PathBuf::from( let data_home = PathBuf::from(
crate::XDG_BASE_DIRS crate::XDG_BASE_DIRS
@ -255,19 +267,6 @@ impl State {
self.start_grpc_server(socket_dir.as_path())?; self.start_grpc_server(socket_dir.as_path())?;
self.system_processes
.refresh_processes_specifics(ProcessRefreshKind::new());
let multiple_instances = self
.system_processes
.processes_by_exact_name("pinnacle")
.filter(|proc| proc.thread_kind().is_none())
.count()
> 1;
let socket_source = PinnacleSocketSource::new(tx_channel, &socket_dir, multiple_instances)
.context("Failed to create socket source")?;
let reload_keybind = metaconfig.reload_keybind; let reload_keybind = metaconfig.reload_keybind;
let kill_keybind = metaconfig.kill_keybind; let kill_keybind = metaconfig.kill_keybind;
@ -324,28 +323,9 @@ impl State {
let reload_keybind = (reload_mask, Keysym::from(reload_keybind.key as u32)); let reload_keybind = (reload_mask, Keysym::from(reload_keybind.key as u32));
let kill_keybind = (kill_mask, Keysym::from(kill_keybind.key as u32)); let kill_keybind = (kill_mask, Keysym::from(kill_keybind.key as u32));
let socket_token = self
.loop_handle
.insert_source(socket_source, |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");
}
})?;
self.input_state.reload_keybind = Some(reload_keybind); self.input_state.reload_keybind = Some(reload_keybind);
self.input_state.kill_keybind = Some(kill_keybind); self.input_state.kill_keybind = Some(kill_keybind);
self.api_state.socket_token = Some(socket_token);
self.config_join_handle = Some(tokio::spawn(async move { self.config_join_handle = Some(tokio::spawn(async move {
let _ = child.wait().await; let _ = child.wait().await;
})); }));

View file

@ -4,12 +4,7 @@ pub mod libinput;
use std::{collections::HashMap, mem::Discriminant}; use std::{collections::HashMap, mem::Discriminant};
use crate::{ use crate::{focus::FocusTarget, state::WithState, window::WindowElement};
api::msg::{CallbackId, Modifier, MouseEdge, OutgoingMsg},
focus::FocusTarget,
state::WithState,
window::WindowElement,
};
use pinnacle_api_defs::pinnacle::input::v0alpha1::{ use pinnacle_api_defs::pinnacle::input::v0alpha1::{
set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse, set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse,
SetMousebindResponse, SetMousebindResponse,
@ -33,8 +28,6 @@ use xkbcommon::xkb::Keysym;
use crate::state::State; use crate::state::State;
use self::libinput::LibinputSetting;
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)]
pub struct ModifierMask: u8 { pub struct ModifierMask: u8 {
@ -85,39 +78,30 @@ impl From<&ModifiersState> for ModifierMask {
#[derive(Default)] #[derive(Default)]
pub struct InputState { pub struct InputState {
/// A hashmap of modifier keys and keycodes to callback IDs pub reload_keybind: Option<(ModifierMask, Keysym)>,
pub keybinds: HashMap<(crate::api::msg::ModifierMask, Keysym), CallbackId>, pub kill_keybind: Option<(ModifierMask, Keysym)>,
/// A hashmap of modifier keys and mouse button codes to callback IDs
pub mousebinds: HashMap<(crate::api::msg::ModifierMask, u32, MouseEdge), CallbackId>,
pub reload_keybind: Option<(crate::api::msg::ModifierMask, Keysym)>,
pub kill_keybind: Option<(crate::api::msg::ModifierMask, Keysym)>,
/// User defined libinput settings that will be applied
pub libinput_settings: Vec<LibinputSetting>,
/// All libinput devices that have been connected /// All libinput devices that have been connected
pub libinput_devices: Vec<input::Device>, pub libinput_devices: Vec<input::Device>,
pub grpc_keybinds: pub keybinds:
HashMap<(ModifierMask, Keysym), UnboundedSender<Result<SetKeybindResponse, tonic::Status>>>, HashMap<(ModifierMask, Keysym), UnboundedSender<Result<SetKeybindResponse, tonic::Status>>>,
pub grpc_mousebinds: HashMap< pub mousebinds: HashMap<
(ModifierMask, u32, set_mousebind_request::MouseEdge), (ModifierMask, u32, set_mousebind_request::MouseEdge),
UnboundedSender<Result<SetMousebindResponse, tonic::Status>>, UnboundedSender<Result<SetMousebindResponse, tonic::Status>>,
>, >,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub grpc_libinput_settings: pub libinput_settings: HashMap<Discriminant<Setting>, Box<dyn Fn(&mut input::Device) + Send>>,
HashMap<Discriminant<Setting>, Box<dyn Fn(&mut input::Device) + Send>>,
} }
impl std::fmt::Debug for InputState { impl std::fmt::Debug for InputState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InputState") f.debug_struct("InputState")
.field("keybinds", &self.keybinds)
.field("mousebinds", &self.mousebinds)
.field("reload_keybind", &self.reload_keybind) .field("reload_keybind", &self.reload_keybind)
.field("kill_keybind", &self.kill_keybind) .field("kill_keybind", &self.kill_keybind)
.field("libinput_settings", &self.libinput_settings)
.field("libinput_devices", &self.libinput_devices) .field("libinput_devices", &self.libinput_devices)
.field("grpc_keybinds", &self.grpc_keybinds) .field("keybinds", &self.keybinds)
.field("grpc_libinput_settings", &"...") .field("mousebinds", &self.mousebinds)
.field("libinput_settings", &"...")
.finish() .finish()
} }
} }
@ -130,9 +114,7 @@ impl InputState {
#[derive(Debug)] #[derive(Debug)]
enum KeyAction { enum KeyAction {
/// Call a callback from a config process CallCallback(UnboundedSender<Result<SetKeybindResponse, tonic::Status>>),
CallCallback(CallbackId),
CallGrpcCallback(UnboundedSender<Result<SetKeybindResponse, tonic::Status>>),
Quit, Quit,
SwitchVt(i32), SwitchVt(i32),
ReloadConfig, ReloadConfig,
@ -257,59 +239,23 @@ impl State {
|state, modifiers, keysym| { |state, modifiers, keysym| {
// tracing::debug!(keysym = ?keysym, raw_keysyms = ?keysym.raw_syms(), modified_syms = ?keysym.modified_syms()); // tracing::debug!(keysym = ?keysym, raw_keysyms = ?keysym.raw_syms(), modified_syms = ?keysym.modified_syms());
if press_state == KeyState::Pressed { if press_state == KeyState::Pressed {
let mut modifier_mask = Vec::<Modifier>::new(); let mod_mask = ModifierMask::from(modifiers);
if modifiers.alt {
modifier_mask.push(Modifier::Alt);
}
if modifiers.shift {
modifier_mask.push(Modifier::Shift);
}
if modifiers.ctrl {
modifier_mask.push(Modifier::Ctrl);
}
if modifiers.logo {
modifier_mask.push(Modifier::Super);
}
let modifier_mask = crate::api::msg::ModifierMask::from(modifier_mask);
let grpc_modifiers = ModifierMask::from(modifiers);
let raw_sym = keysym.raw_syms().iter().next(); let raw_sym = keysym.raw_syms().iter().next();
let mod_sym = keysym.modified_sym(); let mod_sym = keysym.modified_sym();
if let (Some(sender), _) | (None, Some(sender)) = ( if let (Some(sender), _) | (None, Some(sender)) = (
state state.input_state.keybinds.get(&(mod_mask, mod_sym)),
.input_state
.grpc_keybinds
.get(&(grpc_modifiers, mod_sym)),
raw_sym.and_then(|raw_sym| { raw_sym.and_then(|raw_sym| {
state state.input_state.keybinds.get(&(mod_mask, *raw_sym))
.input_state
.grpc_keybinds
.get(&(grpc_modifiers, *raw_sym))
}), }),
) { ) {
return FilterResult::Intercept(KeyAction::CallGrpcCallback( return FilterResult::Intercept(KeyAction::CallCallback(sender.clone()));
sender.clone(),
));
} }
let cb_id_mod = state.input_state.keybinds.get(&(modifier_mask, mod_sym)); if kill_keybind == Some((mod_mask, mod_sym)) {
let cb_id_raw = raw_sym.and_then(|raw_sym| {
state.input_state.keybinds.get(&(modifier_mask, *raw_sym))
});
match (cb_id_mod, cb_id_raw) {
(Some(cb_id), _) | (None, Some(cb_id)) => {
return FilterResult::Intercept(KeyAction::CallCallback(*cb_id));
}
(None, None) => (),
}
if kill_keybind == Some((modifier_mask, mod_sym)) {
return FilterResult::Intercept(KeyAction::Quit); return FilterResult::Intercept(KeyAction::Quit);
} else if reload_keybind == Some((modifier_mask, mod_sym)) { } else if reload_keybind == Some((mod_mask, mod_sym)) {
return FilterResult::Intercept(KeyAction::ReloadConfig); return FilterResult::Intercept(KeyAction::ReloadConfig);
} else if let mut vt @ keysyms::KEY_XF86Switch_VT_1 } else if let mut vt @ keysyms::KEY_XF86Switch_VT_1
..=keysyms::KEY_XF86Switch_VT_12 = keysym.modified_sym().raw() ..=keysyms::KEY_XF86Switch_VT_12 = keysym.modified_sym().raw()
@ -325,20 +271,7 @@ impl State {
); );
match action { match action {
Some(KeyAction::CallCallback(callback_id)) => { Some(KeyAction::CallCallback(sender)) => {
if let Some(stream) = self.api_state.stream.as_ref() {
if let Err(err) = crate::api::send_to_client(
&mut stream.lock().expect("Could not lock stream mutex"),
&OutgoingMsg::CallCallback {
callback_id,
args: None,
},
) {
tracing::error!("error sending msg to client: {err}");
}
}
}
Some(KeyAction::CallGrpcCallback(sender)) => {
let _ = sender.send(Ok(SetKeybindResponse {})); let _ = sender.send(Ok(SetKeybindResponse {}));
} }
Some(KeyAction::SwitchVt(vt)) => { Some(KeyAction::SwitchVt(vt)) => {
@ -367,42 +300,17 @@ impl State {
let pointer_loc = pointer.current_location(); let pointer_loc = pointer.current_location();
let mod_mask = ModifierMask::from(keyboard.modifier_state());
let mouse_edge = match button_state { let mouse_edge = match button_state {
ButtonState::Released => MouseEdge::Release,
ButtonState::Pressed => MouseEdge::Press,
};
let modifier_mask = crate::api::msg::ModifierMask::from(keyboard.modifier_state());
let grpc_modifier_mask = ModifierMask::from(keyboard.modifier_state());
// If any mousebinds are detected, call the config's callback and return.
if let Some(&callback_id) =
self.input_state
.mousebinds
.get(&(modifier_mask, button, mouse_edge))
{
if let Some(stream) = self.api_state.stream.as_ref() {
crate::api::send_to_client(
&mut stream.lock().expect("failed to lock api stream"),
&OutgoingMsg::CallCallback {
callback_id,
args: None,
},
)
.expect("failed to call callback");
}
return;
}
let grpc_mouse_edge = match button_state {
ButtonState::Released => set_mousebind_request::MouseEdge::Release, ButtonState::Released => set_mousebind_request::MouseEdge::Release,
ButtonState::Pressed => set_mousebind_request::MouseEdge::Press, ButtonState::Pressed => set_mousebind_request::MouseEdge::Press,
}; };
if let Some(stream) = if let Some(stream) = self
self.input_state .input_state
.grpc_mousebinds .mousebinds
.get(&(grpc_modifier_mask, button, grpc_mouse_edge)) .get(&(mod_mask, button, mouse_edge))
{ {
let _ = stream.send(Ok(SetMousebindResponse {})); let _ = stream.send(Ok(SetMousebindResponse {}));
} }

View file

@ -1,104 +1,7 @@
use smithay::{ use smithay::backend::{input::InputEvent, libinput::LibinputInputBackend};
backend::{input::InputEvent, libinput::LibinputInputBackend},
reexports::input::{self, AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
};
use crate::state::State; use crate::state::State;
#[derive(Debug, serde::Deserialize)]
#[serde(remote = "AccelProfile")]
enum AccelProfileDef {
Flat,
Adaptive,
}
#[derive(Debug, serde::Deserialize)]
#[serde(remote = "ClickMethod")]
enum ClickMethodDef {
ButtonAreas,
Clickfinger,
}
#[derive(Debug, serde::Deserialize)]
#[serde(remote = "ScrollMethod")]
enum ScrollMethodDef {
NoScroll,
TwoFinger,
Edge,
OnButtonDown,
}
#[derive(Debug, serde::Deserialize)]
#[serde(remote = "TapButtonMap")]
enum TapButtonMapDef {
LeftRightMiddle,
LeftMiddleRight,
}
#[derive(Debug, PartialEq, Copy, Clone, serde::Deserialize)]
pub enum LibinputSetting {
#[serde(with = "AccelProfileDef")]
AccelProfile(AccelProfile),
AccelSpeed(f64),
CalibrationMatrix([f32; 6]),
#[serde(with = "ClickMethodDef")]
ClickMethod(ClickMethod),
DisableWhileTypingEnabled(bool),
LeftHanded(bool),
MiddleEmulationEnabled(bool),
RotationAngle(u32),
#[serde(with = "ScrollMethodDef")]
ScrollMethod(ScrollMethod),
NaturalScrollEnabled(bool),
ScrollButton(u32),
#[serde(with = "TapButtonMapDef")]
TapButtonMap(TapButtonMap),
TapDragEnabled(bool),
TapDragLockEnabled(bool),
TapEnabled(bool),
}
impl LibinputSetting {
pub fn apply_to_device(&self, device: &mut input::Device) {
let _ = match self {
LibinputSetting::AccelProfile(profile) => device.config_accel_set_profile(*profile),
LibinputSetting::AccelSpeed(speed) => device.config_accel_set_speed(*speed),
LibinputSetting::CalibrationMatrix(matrix) => {
device.config_calibration_set_matrix(*matrix)
}
LibinputSetting::ClickMethod(method) => device.config_click_set_method(*method),
LibinputSetting::DisableWhileTypingEnabled(enabled) => {
device.config_dwt_set_enabled(*enabled)
}
LibinputSetting::LeftHanded(enabled) => device.config_left_handed_set(*enabled),
LibinputSetting::MiddleEmulationEnabled(enabled) => {
device.config_middle_emulation_set_enabled(*enabled)
}
LibinputSetting::RotationAngle(angle) => device.config_rotation_set_angle(*angle),
LibinputSetting::ScrollMethod(method) => device.config_scroll_set_method(*method),
LibinputSetting::NaturalScrollEnabled(enabled) => {
device.config_scroll_set_natural_scroll_enabled(*enabled)
}
LibinputSetting::ScrollButton(button) => device.config_scroll_set_button(*button),
LibinputSetting::TapButtonMap(map) => device.config_tap_set_button_map(*map),
LibinputSetting::TapDragEnabled(enabled) => {
device.config_tap_set_drag_enabled(*enabled)
}
LibinputSetting::TapDragLockEnabled(enabled) => {
device.config_tap_set_drag_lock_enabled(*enabled)
}
LibinputSetting::TapEnabled(enabled) => device.config_tap_set_enabled(*enabled),
};
}
}
// We want to completely replace old settings, so we hash only the discriminant.
impl std::hash::Hash for LibinputSetting {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}
impl State { impl State {
/// Apply current libinput settings to new devices. /// Apply current libinput settings to new devices.
pub fn apply_libinput_settings(&mut self, event: &InputEvent<LibinputInputBackend>) { pub fn apply_libinput_settings(&mut self, event: &InputEvent<LibinputInputBackend>) {
@ -117,10 +20,7 @@ impl State {
return; return;
} }
for setting in self.input_state.libinput_settings.iter() { for setting in self.input_state.libinput_settings.values() {
setting.apply_to_device(&mut device);
}
for setting in self.input_state.grpc_libinput_settings.values() {
setting(&mut device); setting(&mut device);
} }

View file

@ -8,7 +8,7 @@
//! While Pinnacle is not a library, this documentation serves to guide those who want to //! While Pinnacle is not a library, this documentation serves to guide those who want to
//! contribute or learn how building something like this works. //! contribute or learn how building something like this works.
// #![deny(unused_imports)] // gonna force myself to keep stuff clean // #![deny(unused_imports)] // this has remained commented out for months lol
#![warn(clippy::unwrap_used)] #![warn(clippy::unwrap_used)]
use clap::Parser; use clap::Parser;

View file

@ -1,22 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use crate::{ use crate::{
api::{msg::Msg, ApiState}, backend::Backend, config::Config, cursor::Cursor, focus::FocusState,
backend::Backend, grab::resize_grab::ResizeSurfaceState, window::WindowElement,
config::Config,
cursor::Cursor,
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
window::WindowElement,
}; };
use smithay::{ use smithay::{
desktop::{PopupManager, Space}, desktop::{PopupManager, Space},
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState}, input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
reexports::{ reexports::{
calloop::{ calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
self, channel::Event, generic::Generic, Interest, LoopHandle, LoopSignal, Mode,
PostAction,
},
wayland_server::{ wayland_server::{
backend::{ClientData, ClientId, DisconnectReason}, backend::{ClientData, ClientId, DisconnectReason},
protocol::wl_surface::WlSurface, protocol::wl_surface::WlSurface,
@ -74,8 +66,6 @@ pub struct State {
/// The state of key and mousebinds along with libinput settings /// The state of key and mousebinds along with libinput settings
pub input_state: InputState, pub input_state: InputState,
/// The state holding stuff dealing with the api, like the stream
pub api_state: ApiState,
/// Keeps track of the focus stack and focused output /// Keeps track of the focus stack and focused output
pub focus_state: FocusState, pub focus_state: FocusState,
@ -159,8 +149,6 @@ impl State {
}, },
)?; )?;
let (tx_channel, rx_channel) = calloop::channel::channel::<Msg>();
loop_handle.insert_idle(|data| { loop_handle.insert_idle(|data| {
if let Err(err) = data.state.start_config(crate::config::get_config_dir()) { if let Err(err) = data.state.start_config(crate::config::get_config_dir()) {
panic!("failed to start config: {err}"); panic!("failed to start config: {err}");
@ -174,16 +162,6 @@ impl State {
seat.add_keyboard(XkbConfig::default(), 500, 25)?; seat.add_keyboard(XkbConfig::default(), 500, 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!(),
})
.expect("failed to insert rx_channel into loop");
});
let xwayland = { let xwayland = {
let (xwayland, channel) = XWayland::new(&display_handle); let (xwayland, channel) = XWayland::new(&display_handle);
let clone = display_handle.clone(); let clone = display_handle.clone();
@ -253,11 +231,6 @@ impl State {
layer_shell_state: WlrLayerShellState::new::<Self>(&display_handle), layer_shell_state: WlrLayerShellState::new::<Self>(&display_handle),
input_state: InputState::new(), input_state: InputState::new(),
api_state: ApiState {
stream: None,
socket_token: None,
tx_channel,
},
focus_state: FocusState::new(), focus_state: FocusState::new(),
config: Config::default(), config: Config::default(),