Add output location setting

This commit is contained in:
Ottatop 2023-08-04 13:45:38 -05:00
parent f9a32af4dc
commit f99c8b886d
8 changed files with 177 additions and 39 deletions

View file

@ -121,6 +121,28 @@ function output_module.tags(op) end
---@see Output.add_tags
function output_module.add_tags(op, ...) end
---Set the specified output's location.
---
---@usage
----- Assuming DP-1 is 2560x1440 and DP-2 is 1920x1080...
---local dp1 = output.get_by_name("DP-1")
---local dp2 = output.get_by_name("DP-2")
---
----- Place DP-2 to the left of DP-1, top borders aligned
---output.set_loc(dp1, { x = 1920, y = 0 })
---output.set_loc(dp2, { x = 0, y = 0 })
---
----- Do the same as above, with a different origin
---output.set_loc(dp1, { x = 0, y = 0 })
---output.set_loc(dp2, { x = -1920, y = 0 })
---
----- Place DP-2 to the right of DP-1, bottom borders aligned
---output.set_loc(dp1, { x = 0, y = 0 })
---output.set_loc(dp2, { x = 2560, y = 1440 - 1080 })
---@tparam Output op
---@tparam table loc A table of the form `{ x: integer?, y: integer? }`
function output_module.set_loc(op, loc) end
----------------------------------------------------------
---The output object.
@ -176,3 +198,24 @@ function output:physical_size() end
---@treturn boolean|nil
---@see OutputModule.focused
function output:focused() end
---Set this output's location.
---
---@usage
--- -- Assuming DP-1 is 2560x1440 and DP-2 is 1920x1080...
---local dp1 = output.get_by_name("DP-1")
---local dp2 = output.get_by_name("DP-2")
---
--- -- Place DP-2 to the left of DP-1, top borders aligned
---dp1:set_loc({ x = 1920, y = 0 })
---dp2:set_loc({ x = 0, y = 0 })
---
--- -- Do the same as above, with a different origin
---dp1:set_loc({ x = 0, y = 0 })
---dp2:set_loc({ x = -1920, y = 0 })
---
--- -- Place DP-2 to the right of DP-1, bottom borders aligned
---dp1:set_loc({ x = 0, y = 0 })
---dp2:set_loc({ x = 2560, y = 1440 - 1080 })
---@tparam table loc A table of the form `{ x: integer?, y: integer? }`
function output:set_loc(loc) end

View file

@ -21,6 +21,7 @@
---@field SetLayout { tag_id: TagId, layout: Layout }?
--Outputs
---@field ConnectForAllOutputs { callback_id: integer }?
---@field SetOutputLocation { output_name: OutputName, x: integer?, y: integer? }?
---@alias Msg _Msg | "Quit"

View file

@ -93,6 +93,31 @@ function output:focused()
return output_module.focused(self)
end
---Set this output's location.
---
---### Examples
---```lua
----- Assuming DP-1 is 2560x1440 and DP-2 is 1920x1080...
---local dp1 = output.get_by_name("DP-1")
---local dp2 = output.get_by_name("DP-2")
---
----- Place DP-2 to the left of DP-1, top borders aligned
---dp1:set_loc({ x = 1920, y = 0 })
---dp2:set_loc({ x = 0, y = 0 })
---
----- Do the same as above, with a different origin
---dp1:set_loc({ x = 0, y = 0 })
---dp2:set_loc({ x = -1920, y = 0 })
---
----- Place DP-2 to the right of DP-1, bottom borders aligned
---dp1:set_loc({ x = 0, y = 0 })
---dp2:set_loc({ x = 2560, y = 1440 - 1080 })
---```
---@param loc { x: integer?, y: integer? }
function output:set_loc(loc)
return output_module.set_loc(self, loc)
end
------------------------------------------------------
---Get an output by its name.
@ -104,7 +129,7 @@ end
---### Example
---```lua
---local monitor = output.get_by_name("DP-1")
---print(monitor.name) -- should print `DP-1`
---print(monitor:name()) -- should print `DP-1`
---```
---@param name string The name of the output.
---@return Output|nil output The output, or nil if none have the provided name.
@ -240,6 +265,8 @@ function output_module.get_for_tag(tag)
end
end
---------Fully-qualified functions
---Get the specified output's make.
---@param op Output
---@return string|nil
@ -369,4 +396,36 @@ function output_module.add_tags(op, ...)
require("tag").add(op, ...)
end
---Set the specified output's location.
---
---### Examples
---```lua
----- Assuming DP-1 is 2560x1440 and DP-2 is 1920x1080...
---local dp1 = output.get_by_name("DP-1")
---local dp2 = output.get_by_name("DP-2")
---
----- Place DP-2 to the left of DP-1, top borders aligned
---output.set_loc(dp1, { x = 1920, y = 0 })
---output.set_loc(dp2, { x = 0, y = 0 })
---
----- Do the same as above, with a different origin
---output.set_loc(dp1, { x = 0, y = 0 })
---output.set_loc(dp2, { x = -1920, y = 0 })
---
----- Place DP-2 to the right of DP-1, bottom borders aligned
---output.set_loc(dp1, { x = 0, y = 0 })
---output.set_loc(dp2, { x = 2560, y = 1440 - 1080 })
---```
---@param op Output
---@param loc { x: integer?, y: integer? }
function output_module.set_loc(op, loc)
SendMsg({
SetOutputLocation = {
output_name = op:name(),
x = loc.x,
y = loc.y,
},
})
end
return output_module

View file

@ -66,10 +66,20 @@ require("pinnacle").setup(function(pinnacle)
-- Just testing stuff
input.keybind({ mod_key }, keys.h, function()
local win = window.get_focused()
if win ~= nil then
win:set_size({ w = 500, h = 500 })
end
local dp2 = output.get_by_name("DP-2")
local dp3 = output.get_by_name("DP-3")
output.set_loc(dp2, { x = 1920, y = 0 })
output.set_loc(dp3, { x = 0, y = 1440 - 1080 })
local dp2loc = dp2:loc()
local dp3loc = dp3:loc()
print("dp2: " .. dp2loc.x .. ", " .. dp2loc.y)
print("dp3: " .. dp3loc.x .. ", " .. dp3loc.y)
-- local win = window.get_focused()
-- if win ~= nil then
-- win:set_size({ w = 500, h = 500 })
-- end
-- local wins = window.get_all()
-- for _, win in pairs(wins) do

View file

@ -3,7 +3,7 @@
// 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 crate::{layout::Layout, tag::TagId, window::window_state::WindowId};
use crate::{layout::Layout, output::OutputName, tag::TagId, window::window_state::WindowId};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32);
@ -68,6 +68,13 @@ pub enum Msg {
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.

View file

@ -440,6 +440,7 @@ impl State<UdevData> {
// InputEvent::DeviceRemoved { device } => todo!(),
InputEvent::Keyboard { event } => self.keyboard::<B>(event),
InputEvent::PointerMotion { event } => self.pointer_motion::<B>(event),
// currently does not seem to use absolute
InputEvent::PointerMotionAbsolute { event } => self.pointer_motion_absolute::<B>(event),
InputEvent::PointerButton { event } => self.pointer_button::<B>(event),
InputEvent::PointerAxis { event } => self.pointer_axis::<B>(event),
@ -557,38 +558,26 @@ impl State<UdevData> {
}
let (pos_x, pos_y) = pos.into();
let max_x = self.space.outputs().fold(0, |acc, o| {
acc + self
let nearest_points = self.space.outputs().map(|op| {
let size = self
.space
.output_geometry(o)
.expect("Output geometry doesn't exist")
.size
.w
});
let clamped_x = pos_x.clamp(0.0, max_x as f64);
let max_y = self
.space
.outputs()
.find(|o| {
let geo = self
.space
.output_geometry(o)
.expect("Output geometry doesn't exist");
geo.contains((clamped_x as i32, 0))
})
.map(|o| {
self.space
.output_geometry(o)
.expect("Output geometry doesn't exist")
.size
.h
.output_geometry(op)
.expect("called output_geometry on unmapped output")
.size;
let loc = op.current_location();
let pos_x = pos_x.clamp(loc.x as f64, (loc.x + size.w) as f64);
let pos_y = pos_y.clamp(loc.y as f64, (loc.y + size.h) as f64);
(pos_x, pos_y)
});
if let Some(max_y) = max_y {
let clamped_y = pos_y.clamp(0.0, max_y as f64);
(clamped_x, clamped_y).into()
} else {
(clamped_x, pos_y).into()
}
let nearest_point = nearest_points.min_by(|(x1, y1), (x2, y2)| {
f64::total_cmp(
&((pos_x - x1).powi(2) + (pos_y - y1).powi(2)).sqrt(),
&((pos_x - x2).powi(2) + (pos_y - y2).powi(2)).sqrt(),
)
});
nearest_point.map(|point| point.into()).unwrap_or(pos)
}
}

View file

@ -4,11 +4,26 @@ use std::cell::RefCell;
use smithay::output::Output;
use crate::{state::WithState, tag::Tag};
use crate::{
backend::Backend,
state::{State, WithState},
tag::Tag,
};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct OutputName(pub String);
impl OutputName {
/// Get the output with this name.
pub fn output<B: Backend>(&self, state: &State<B>) -> Option<Output> {
state
.space
.outputs()
.find(|output| output.name() == self.0)
.cloned()
}
}
#[derive(Default)]
pub struct OutputState {
pub tags: Vec<Tag>,

View file

@ -272,6 +272,20 @@ impl<B: Backend> State<B> {
}
self.output_callback_ids.push(callback_id);
}
Msg::SetOutputLocation { output_name, x, y } => {
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.re_layout(&output);
}
Msg::Quit => {
self.loop_signal.stop();