mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Add output modes to API
This commit is contained in:
parent
869a2223f5
commit
2fd98301e6
4 changed files with 197 additions and 86 deletions
|
@ -12,6 +12,7 @@ local service = prefix .. "OutputService"
|
|||
---@enum (key) OutputServiceMethod
|
||||
local rpc_types = {
|
||||
SetLocation = {},
|
||||
SetMode = {},
|
||||
ConnectForAll = {
|
||||
response_type = "ConnectForAllResponse",
|
||||
},
|
||||
|
@ -319,51 +320,65 @@ function OutputHandle:set_loc_adj_to(other, alignment)
|
|||
---@type integer
|
||||
local y
|
||||
|
||||
if not self_props.current_mode or not other_props.current_mode then
|
||||
return
|
||||
end
|
||||
|
||||
local self_width = self_props.current_mode.pixel_width
|
||||
local self_height = self_props.current_mode.pixel_height
|
||||
local other_width = other_props.current_mode.pixel_width
|
||||
local other_height = other_props.current_mode.pixel_height
|
||||
|
||||
if dir == "top" or dir == "bottom" then
|
||||
if dir == "top" then
|
||||
y = other_props.y - self_props.pixel_height
|
||||
y = other_props.y - self_height
|
||||
else
|
||||
y = other_props.y + other_props.pixel_height
|
||||
y = other_props.y + other_height
|
||||
end
|
||||
|
||||
if align == "left" then
|
||||
x = other_props.x
|
||||
elseif align == "center" then
|
||||
x = other_props.x + math.floor((other_props.pixel_width - self_props.pixel_width) / 2)
|
||||
x = other_props.x + math.floor((other_width - self_width) / 2)
|
||||
elseif align == "bottom" then
|
||||
x = other_props.x + (other_props.pixel_width - self_props.pixel_width)
|
||||
x = other_props.x + (other_width - self_width)
|
||||
end
|
||||
else
|
||||
if dir == "left" then
|
||||
x = other_props.x - self_props.pixel_width
|
||||
x = other_props.x - self_width
|
||||
else
|
||||
x = other_props.x + other_props.pixel_width
|
||||
x = other_props.x + other_width
|
||||
end
|
||||
|
||||
if align == "top" then
|
||||
y = other_props.y
|
||||
elseif align == "center" then
|
||||
y = other_props.y + math.floor((other_props.pixel_height - self_props.pixel_height) / 2)
|
||||
y = other_props.y + math.floor((other_height - self_height) / 2)
|
||||
elseif align == "bottom" then
|
||||
y = other_props.y + (other_props.pixel_height - self_props.pixel_height)
|
||||
y = other_props.y + (other_height - self_height)
|
||||
end
|
||||
end
|
||||
|
||||
self:set_location({ x = x, y = y })
|
||||
end
|
||||
|
||||
---@class Mode
|
||||
---@field pixel_width integer
|
||||
---@field pixel_height integer
|
||||
---@field refresh_rate_millihz integer
|
||||
|
||||
---@class OutputProperties
|
||||
---@field make string?
|
||||
---@field model string?
|
||||
---@field x integer?
|
||||
---@field y integer?
|
||||
---@field pixel_width integer?
|
||||
---@field pixel_height integer?
|
||||
---@field refresh_rate integer?
|
||||
---@field current_mode Mode?
|
||||
---@field preferred_mode Mode?
|
||||
---@field modes Mode[]
|
||||
---@field physical_width integer?
|
||||
---@field physical_height integer?
|
||||
---@field focused boolean?
|
||||
---@field tags TagHandle[]?
|
||||
---@field tags TagHandle[]
|
||||
|
||||
---Get all properties of this output.
|
||||
---
|
||||
|
@ -376,6 +391,7 @@ function OutputHandle:props()
|
|||
|
||||
response.tags = handles
|
||||
response.tag_ids = nil
|
||||
response.modes = response.modes or {}
|
||||
|
||||
return response
|
||||
end
|
||||
|
@ -420,33 +436,31 @@ function OutputHandle:y()
|
|||
return self:props().y
|
||||
end
|
||||
|
||||
---Get this output's width in pixels.
|
||||
---Get this output's current mode.
|
||||
---
|
||||
---Shorthand for `handle:props().pixel_width`.
|
||||
---Shorthand for `handle:props().current_mode`.
|
||||
---
|
||||
---@return integer?
|
||||
function OutputHandle:pixel_width()
|
||||
return self:props().pixel_width
|
||||
---@return Mode?
|
||||
function OutputHandle:current_mode()
|
||||
return self:props().current_mode
|
||||
end
|
||||
|
||||
---Get this output's height in pixels.
|
||||
---Get this output's preferred mode.
|
||||
---
|
||||
---Shorthand for `handle:props().pixel_height`.
|
||||
---Shorthand for `handle:props().preferred_mode`.
|
||||
---
|
||||
---@return integer?
|
||||
function OutputHandle:pixel_height()
|
||||
return self:props().pixel_height
|
||||
---@return Mode?
|
||||
function OutputHandle:preferred_mode()
|
||||
return self:props().preferred_mode
|
||||
end
|
||||
|
||||
---Get this output's refresh rate in millihertz.
|
||||
---Get all of this output's available modes.
|
||||
---
|
||||
---For example, 144Hz is returned as 144000.
|
||||
---Shorthand for `handle:props().modes`.
|
||||
---
|
||||
---Shorthand for `handle:props().refresh_rate`.
|
||||
---
|
||||
---@return integer?
|
||||
function OutputHandle:refresh_rate()
|
||||
return self:props().refresh_rate
|
||||
---@return Mode[]
|
||||
function OutputHandle:modes()
|
||||
return self:props().modes
|
||||
end
|
||||
|
||||
---Get this output's physical width in millimeters.
|
||||
|
|
|
@ -4,12 +4,23 @@ package pinnacle.output.v0alpha1;
|
|||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
message Mode {
|
||||
optional uint32 pixel_width = 1;
|
||||
optional uint32 pixel_height = 2;
|
||||
optional uint32 refresh_rate_millihz = 3;
|
||||
}
|
||||
|
||||
message SetLocationRequest {
|
||||
optional string output_name = 1;
|
||||
optional int32 x = 2;
|
||||
optional int32 y = 3;
|
||||
}
|
||||
|
||||
message SetModeRequest {
|
||||
optional string output_name = 1;
|
||||
optional Mode mode = 2;
|
||||
}
|
||||
|
||||
message GetRequest {}
|
||||
message GetResponse {
|
||||
repeated string output_names = 1;
|
||||
|
@ -19,13 +30,24 @@ message GetPropertiesRequest {
|
|||
optional string output_name = 1;
|
||||
}
|
||||
message GetPropertiesResponse {
|
||||
// The monitor's manufacturer
|
||||
optional string make = 1;
|
||||
// The model of the monitor
|
||||
optional string model = 2;
|
||||
// The x-coord of the output in the global space
|
||||
optional int32 x = 3;
|
||||
// The y coord of the output in the global space
|
||||
optional int32 y = 4;
|
||||
optional uint32 pixel_width = 5;
|
||||
optional uint32 pixel_height = 6;
|
||||
optional uint32 refresh_rate = 7;
|
||||
// NULLABLE
|
||||
//
|
||||
// The current mode
|
||||
optional Mode current_mode = 5;
|
||||
// NULLABLE
|
||||
//
|
||||
// The preferred mode
|
||||
optional Mode preferred_mode = 6;
|
||||
// All available modes
|
||||
repeated Mode modes = 7;
|
||||
// In millimeters
|
||||
optional uint32 physical_width = 8;
|
||||
// In millimeters
|
||||
|
@ -36,6 +58,7 @@ message GetPropertiesResponse {
|
|||
|
||||
service OutputService {
|
||||
rpc SetLocation(SetLocationRequest) returns (google.protobuf.Empty);
|
||||
rpc SetMode(SetModeRequest) returns (google.protobuf.Empty);
|
||||
rpc Get(GetRequest) returns (GetResponse);
|
||||
rpc GetProperties(GetPropertiesRequest) returns (GetPropertiesResponse);
|
||||
}
|
||||
|
|
|
@ -300,11 +300,13 @@ impl OutputHandle {
|
|||
let attempt_set_loc = || -> Option<()> {
|
||||
let other_x = other_props.x?;
|
||||
let other_y = other_props.y?;
|
||||
let other_width = other_props.pixel_width? as i32;
|
||||
let other_height = other_props.pixel_height? as i32;
|
||||
let other_mode = other_props.current_mode?;
|
||||
let other_width = other_mode.pixel_width as i32;
|
||||
let other_height = other_mode.pixel_height as i32;
|
||||
|
||||
let self_width = self_props.pixel_width? as i32;
|
||||
let self_height = self_props.pixel_height? as i32;
|
||||
let self_mode = self_props.current_mode?;
|
||||
let self_width = self_mode.pixel_width as i32;
|
||||
let self_height = self_mode.pixel_height as i32;
|
||||
|
||||
use Alignment::*;
|
||||
|
||||
|
@ -399,9 +401,31 @@ impl OutputHandle {
|
|||
model: response.model,
|
||||
x: response.x,
|
||||
y: response.y,
|
||||
pixel_width: response.pixel_width,
|
||||
pixel_height: response.pixel_height,
|
||||
refresh_rate: response.refresh_rate,
|
||||
current_mode: response.current_mode.and_then(|mode| {
|
||||
Some(Mode {
|
||||
pixel_width: mode.pixel_width?,
|
||||
pixel_height: mode.pixel_height?,
|
||||
refresh_rate_millihertz: mode.refresh_rate_millihz?,
|
||||
})
|
||||
}),
|
||||
preferred_mode: response.preferred_mode.and_then(|mode| {
|
||||
Some(Mode {
|
||||
pixel_width: mode.pixel_width?,
|
||||
pixel_height: mode.pixel_height?,
|
||||
refresh_rate_millihertz: mode.refresh_rate_millihz?,
|
||||
})
|
||||
}),
|
||||
modes: response
|
||||
.modes
|
||||
.into_iter()
|
||||
.flat_map(|mode| {
|
||||
Some(Mode {
|
||||
pixel_width: mode.pixel_width?,
|
||||
pixel_height: mode.pixel_height?,
|
||||
refresh_rate_millihertz: mode.refresh_rate_millihz?,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
physical_width: response.physical_width,
|
||||
physical_height: response.physical_height,
|
||||
focused: response.focused,
|
||||
|
@ -463,42 +487,40 @@ impl OutputHandle {
|
|||
self.props_async().await.y
|
||||
}
|
||||
|
||||
/// Get this output's screen width in pixels.
|
||||
/// Get this output's current mode.
|
||||
///
|
||||
/// Shorthand for `self.props().pixel_width`.
|
||||
pub fn pixel_width(&self) -> Option<u32> {
|
||||
self.props().pixel_width
|
||||
/// Shorthand for `self.props().current_mode`.
|
||||
pub fn current_mode(&self) -> Option<Mode> {
|
||||
self.props().current_mode
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::pixel_width`].
|
||||
pub async fn pixel_width_async(&self) -> Option<u32> {
|
||||
self.props_async().await.pixel_width
|
||||
/// The async version of [`OutputHandle::current_mode`].
|
||||
pub async fn current_mode_async(&self) -> Option<Mode> {
|
||||
self.props_async().await.current_mode
|
||||
}
|
||||
|
||||
/// Get this output's screen height in pixels.
|
||||
/// Get this output's preferred mode.
|
||||
///
|
||||
/// Shorthand for `self.props().pixel_height`.
|
||||
pub fn pixel_height(&self) -> Option<u32> {
|
||||
self.props().pixel_height
|
||||
/// Shorthand for `self.props().preferred_mode`.
|
||||
pub fn preferred_mode(&self) -> Option<Mode> {
|
||||
self.props().preferred_mode
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::pixel_height`].
|
||||
pub async fn pixel_height_async(&self) -> Option<u32> {
|
||||
self.props_async().await.pixel_height
|
||||
/// The async version of [`OutputHandle::preferred_mode`].
|
||||
pub async fn preferred_mode_async(&self) -> Option<Mode> {
|
||||
self.props_async().await.preferred_mode
|
||||
}
|
||||
|
||||
/// Get this output's refresh rate in millihertz.
|
||||
/// Get all available modes this output supports.
|
||||
///
|
||||
/// For example, 144Hz will be returned as 144000.
|
||||
///
|
||||
/// Shorthand for `self.props().refresh_rate`.
|
||||
pub fn refresh_rate(&self) -> Option<u32> {
|
||||
self.props().refresh_rate
|
||||
/// Shorthand for `self.props().modes`.
|
||||
pub fn modes(&self) -> Vec<Mode> {
|
||||
self.props().modes
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::refresh_rate`].
|
||||
pub async fn refresh_rate_async(&self) -> Option<u32> {
|
||||
self.props_async().await.refresh_rate
|
||||
/// The async version of [`OutputHandle::modes`].
|
||||
pub async fn modes_async(&self) -> Vec<Mode> {
|
||||
self.props_async().await.modes
|
||||
}
|
||||
|
||||
/// Get this output's physical width in millimeters.
|
||||
|
@ -557,34 +579,47 @@ impl OutputHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// A possible output pixel dimension and refresh rate configuration.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
|
||||
pub struct Mode {
|
||||
/// The width of the output, in pixels.
|
||||
pub pixel_width: u32,
|
||||
/// The height of the output, in pixels.
|
||||
pub pixel_height: u32,
|
||||
/// The output's refresh rate, in millihertz.
|
||||
///
|
||||
/// For example, 60Hz is returned as 60000.
|
||||
pub refresh_rate_millihertz: u32,
|
||||
}
|
||||
|
||||
/// The properties of an output.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct OutputProperties {
|
||||
/// The make of the output
|
||||
/// The make of the output.
|
||||
pub make: Option<String>,
|
||||
/// The model of the output
|
||||
/// The model of the output.
|
||||
///
|
||||
/// This is something like "27GL83A" or whatever crap monitor manufacturers name their monitors
|
||||
/// these days.
|
||||
pub model: Option<String>,
|
||||
/// The x position of the output in the global space
|
||||
/// The x position of the output in the global space.
|
||||
pub x: Option<i32>,
|
||||
/// The y position of the output in the global space
|
||||
/// The y position of the output in the global space.
|
||||
pub y: Option<i32>,
|
||||
/// The output's screen width in pixels
|
||||
pub pixel_width: Option<u32>,
|
||||
/// The output's screen height in pixels
|
||||
pub pixel_height: Option<u32>,
|
||||
/// The output's refresh rate in millihertz
|
||||
pub refresh_rate: Option<u32>,
|
||||
/// The output's physical width in millimeters
|
||||
/// The output's current mode.
|
||||
pub current_mode: Option<Mode>,
|
||||
/// The output's preferred mode.
|
||||
pub preferred_mode: Option<Mode>,
|
||||
/// All available modes the output supports.
|
||||
pub modes: Vec<Mode>,
|
||||
/// The output's physical width in millimeters.
|
||||
pub physical_width: Option<u32>,
|
||||
/// The output's physical height in millimeters
|
||||
/// The output's physical height in millimeters.
|
||||
pub physical_height: Option<u32>,
|
||||
/// Whether this output is focused or not
|
||||
/// Whether this output is focused or not.
|
||||
///
|
||||
/// This is currently implemented as the output with the most recent pointer motion.
|
||||
pub focused: Option<bool>,
|
||||
/// The tags this output has
|
||||
/// The tags this output has.
|
||||
pub tags: Vec<TagHandle>,
|
||||
}
|
||||
|
|
59
src/api.rs
59
src/api.rs
|
@ -14,7 +14,7 @@ use pinnacle_api_defs::pinnacle::{
|
|||
},
|
||||
output::{
|
||||
self,
|
||||
v0alpha1::{output_service_server, SetLocationRequest},
|
||||
v0alpha1::{output_service_server, SetLocationRequest, SetModeRequest},
|
||||
},
|
||||
process::v0alpha1::{process_service_server, SetEnvRequest, SpawnRequest, SpawnResponse},
|
||||
tag::{
|
||||
|
@ -949,6 +949,30 @@ impl output_service_server::OutputService for OutputService {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn set_mode(&self, request: Request<SetModeRequest>) -> Result<Response<()>, Status> {
|
||||
let request = request.into_inner();
|
||||
|
||||
run_unary_no_response(&self.sender, |state| {
|
||||
let Some(output) = request
|
||||
.output_name
|
||||
.map(OutputName)
|
||||
.and_then(|name| name.output(state))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mode = request.mode.and_then(|mode| {
|
||||
Some(smithay::output::Mode {
|
||||
size: (mode.pixel_width? as i32, mode.pixel_height? as i32).into(),
|
||||
refresh: mode.refresh_rate_millihz? as i32,
|
||||
})
|
||||
});
|
||||
|
||||
output.change_current_state(mode, None, None, None);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get(
|
||||
&self,
|
||||
_request: Request<output::v0alpha1::GetRequest>,
|
||||
|
@ -977,20 +1001,35 @@ impl output_service_server::OutputService for OutputService {
|
|||
.ok_or_else(|| Status::invalid_argument("no output specified"))?,
|
||||
);
|
||||
|
||||
let from_smithay_mode = |mode: smithay::output::Mode| -> output::v0alpha1::Mode {
|
||||
output::v0alpha1::Mode {
|
||||
pixel_width: Some(mode.size.w as u32),
|
||||
pixel_height: Some(mode.size.h as u32),
|
||||
refresh_rate_millihz: Some(mode.refresh as u32),
|
||||
}
|
||||
};
|
||||
|
||||
run_unary(&self.sender, move |state| {
|
||||
let output = output_name.output(state);
|
||||
|
||||
let pixel_width = output
|
||||
let current_mode = output
|
||||
.as_ref()
|
||||
.and_then(|output| output.current_mode().map(|mode| mode.size.w as u32));
|
||||
.and_then(|output| output.current_mode().map(from_smithay_mode));
|
||||
|
||||
let pixel_height = output
|
||||
let preferred_mode = output
|
||||
.as_ref()
|
||||
.and_then(|output| output.current_mode().map(|mode| mode.size.h as u32));
|
||||
.and_then(|output| output.preferred_mode().map(from_smithay_mode));
|
||||
|
||||
let refresh_rate = output
|
||||
let modes = output
|
||||
.as_ref()
|
||||
.and_then(|output| output.current_mode().map(|mode| mode.refresh as u32));
|
||||
.map(|output| {
|
||||
output
|
||||
.modes()
|
||||
.into_iter()
|
||||
.map(from_smithay_mode)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
let model = output
|
||||
.as_ref()
|
||||
|
@ -1030,9 +1069,9 @@ impl output_service_server::OutputService for OutputService {
|
|||
model,
|
||||
x,
|
||||
y,
|
||||
pixel_width,
|
||||
pixel_height,
|
||||
refresh_rate,
|
||||
current_mode,
|
||||
preferred_mode,
|
||||
modes,
|
||||
physical_width,
|
||||
physical_height,
|
||||
focused,
|
||||
|
|
Loading…
Add table
Reference in a new issue