diff --git a/api/lua/pinnacle/output.lua b/api/lua/pinnacle/output.lua index 87706f9..c20f063 100644 --- a/api/lua/pinnacle/output.lua +++ b/api/lua/pinnacle/output.lua @@ -167,6 +167,19 @@ function output.connect_for_all(callback) }) end +---@param id_str string +---@param op OutputHandle +--- +---@return boolean +local function output_id_matches(id_str, op) + if id_str:match("^serial:") then + local serial = tonumber(id_str:sub(8)) + return serial and serial == op:serial() or false + else + return id_str == op.name + end +end + ---@class OutputSetup ---@field filter (fun(output: OutputHandle): boolean)? ---@field mode Mode? @@ -242,7 +255,9 @@ function output.setup(setups) if op_id:match("^%d+:") then ---@type string local index = op_id:match("^%d+") + ---@diagnostic disable-next-line: redefined-local local op_id = op_id:sub(index:len() + 2) + ---@diagnostic disable-next-line: redefined-local local index = tonumber(index) ---@cast index number @@ -280,7 +295,7 @@ function output.setup(setups) ---@param op OutputHandle local function apply_setups(op) for _, op_setup in ipairs(op_setups) do - if op_setup[1] == op.name or op_setup[1] == "*" then + if output_id_matches(op_setup[1], op) or op_setup[1] == "*" then local setup = op_setup.setup if setup.filter and not setup.filter(op) then @@ -380,7 +395,9 @@ function output.setup_locs(update_locs_on, locs) if op_id:match("^%d+:") then ---@type string local index = op_id:match("^%d+") + ---@diagnostic disable-next-line: redefined-local local op_id = op_id:sub(index:len() + 2) + ---@diagnostic disable-next-line: redefined-local local index = tonumber(index) ---@cast index number @@ -417,7 +434,7 @@ function output.setup_locs(update_locs_on, locs) ---@diagnostic disable-next-line: redefined-local for _, setup in ipairs(setups) do for _, op in ipairs(outputs) do - if op.name == setup[1] then + if output_id_matches(setup[1], op) then if type(setup.loc[1]) == "number" then local loc = { x = setup.loc[1], y = setup.loc[2] } op:set_location(loc) @@ -445,7 +462,7 @@ function output.setup_locs(update_locs_on, locs) end end - if op.name ~= setup[1] or type(setup.loc[1]) == "number" then + if not output_id_matches(setup[1], op) or type(setup.loc[1]) == "number" then goto continue end @@ -1028,7 +1045,7 @@ function OutputHandle:transform() return self:props().transform end ----Get this output's serial. +---Get this output's EDID serial number. --- ---Shorthand for `handle:props().serial`. --- diff --git a/api/rust/src/output.rs b/api/rust/src/output.rs index 1fd1895..5db7ced 100644 --- a/api/rust/src/output.rs +++ b/api/rust/src/output.rs @@ -9,7 +9,7 @@ //! This module provides [`Output`], which allows you to get [`OutputHandle`]s for different //! connected monitors and set them up. -use std::sync::OnceLock; +use std::{num::NonZeroU32, sync::OnceLock}; use futures::FutureExt; use pinnacle_api_defs::pinnacle::output::{ @@ -516,7 +516,13 @@ pub enum OutputLoc { pub enum OutputId { /// Identify using the output's name. Name(String), - // TODO: serial + /// Identify using the output's EDID serial number. + /// + /// Note: some displays (like laptop screens) don't have a serial number, in which case this won't match it. + /// Additionally the Rust API assumes monitor serial numbers are unique. + /// If you're unlucky enough to have two monitors with the same serial number, + /// use [`OutputId::Name`] instead. + Serial(NonZeroU32), } impl OutputId { @@ -532,6 +538,7 @@ impl OutputId { pub fn matches(&self, output: &OutputHandle) -> bool { match self { OutputId::Name(name) => name == output.name(), + OutputId::Serial(serial) => Some(serial.get()) == output.serial(), } } } @@ -1111,6 +1118,30 @@ impl OutputHandle { self.props_async().await.scale } + /// Get this output's transform. + /// + /// Shorthand for `self.props().transform` + pub fn transform(&self) -> Option { + self.props().transform + } + + /// The async version of [`OutputHandle::transform`]. + pub async fn transform_async(&self) -> Option { + self.props_async().await.transform + } + + /// Get this output's EDID serial number. + /// + /// Shorthand for `self.props().serial` + pub fn serial(&self) -> Option { + self.props().serial + } + + /// The async version of [`OutputHandle::serial`]. + pub async fn serial_async(&self) -> Option { + self.props_async().await.serial + } + /// Get this output's unique name (the name of its connector). pub fn name(&self) -> &str { &self.name diff --git a/src/output.rs b/src/output.rs index ab47bfe..a96dec9 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use std::{num::NonZeroU32, sync::RwLock}; +use std::{cell::RefCell, num::NonZeroU32}; use pinnacle_api_defs::pinnacle::signal::v0alpha1::{OutputMoveResponse, OutputResizeResponse}; use smithay::{ @@ -53,9 +53,9 @@ impl WithState for Output { { let state = self .user_data() - .get_or_insert_threadsafe(RwLock::::default); + .get_or_insert(RefCell::::default); - func(&state.read().expect("with_state already locked")) + func(&state.borrow()) } fn with_state_mut(&self, func: F) -> T @@ -64,9 +64,9 @@ impl WithState for Output { { let state = self .user_data() - .get_or_insert_threadsafe(RwLock::::default); + .get_or_insert(RefCell::::default); - func(&mut state.write().expect("with_state already locked")) + func(&mut state.borrow_mut()) } } diff --git a/src/state.rs b/src/state.rs index ed890f3..0512395 100644 --- a/src/state.rs +++ b/src/state.rs @@ -40,11 +40,7 @@ use smithay::{ }, xwayland::{X11Wm, XWayland, XWaylandEvent}, }; -use std::{ - path::PathBuf, - sync::{Arc, RwLock}, - time::Duration, -}; +use std::{cell::RefCell, path::PathBuf, sync::Arc, time::Duration}; use sysinfo::{ProcessRefreshKind, RefreshKind}; use tracing::{error, info}; use xdg::BaseDirectories; @@ -382,9 +378,9 @@ impl WithState for WlSurface { compositor::with_states(self, |states| { let state = states .data_map - .get_or_insert_threadsafe(RwLock::::default); + .get_or_insert(RefCell::::default); - func(&state.read().expect("with_state already locked")) + func(&state.borrow()) }) } @@ -395,9 +391,9 @@ impl WithState for WlSurface { compositor::with_states(self, |states| { let state = states .data_map - .get_or_insert_threadsafe(RwLock::::default); + .get_or_insert(RefCell::::default); - func(&mut state.write().expect("with_state already locked")) + func(&mut state.borrow_mut()) }) } } diff --git a/src/window.rs b/src/window.rs index f25d1f8..bce6e4a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -2,7 +2,7 @@ pub mod rules; -use std::{ops::Deref, sync::RwLock}; +use std::{cell::RefCell, ops::Deref}; use smithay::{ desktop::{space::SpaceElement, Window, WindowSurface}, @@ -192,9 +192,9 @@ impl WithState for WindowElement { { let state = self .user_data() - .get_or_insert_threadsafe(|| RwLock::new(WindowElementState::new())); + .get_or_insert(|| RefCell::new(WindowElementState::new())); - func(&state.read().expect("with_state already locked")) + func(&state.borrow()) } fn with_state_mut(&self, func: F) -> T @@ -203,9 +203,9 @@ impl WithState for WindowElement { { let state = self .user_data() - .get_or_insert(|| RwLock::new(WindowElementState::new())); + .get_or_insert(|| RefCell::new(WindowElementState::new())); - func(&mut state.write().expect("with_state already locked")) + func(&mut state.borrow_mut()) } }