mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Add output resize and move signals
This commit is contained in:
parent
6d78436d18
commit
023ebe8a2d
11 changed files with 207 additions and 18 deletions
|
@ -166,12 +166,17 @@ function output.connect_for_all(callback)
|
|||
})
|
||||
end
|
||||
|
||||
---@type table<string, SignalServiceMethod>
|
||||
local signal_name_to_SignalName = {
|
||||
connect = "OutputConnect",
|
||||
resize = "OutputResize",
|
||||
move = "OutputMove",
|
||||
}
|
||||
|
||||
---@class OutputSignal Signals related to output events.
|
||||
---@field connect fun(output: OutputHandle)? An output was connected. FIXME: This currently does not fire for outputs that have been previously connected and disconnected.
|
||||
---@field resize fun(output: OutputHandle, logical_width: integer, logical_height: integer)? An output's logical size changed.
|
||||
---@field move fun(output: OutputHandle, x: integer, y: integer)? An output moved.
|
||||
|
||||
---Connect to an output signal.
|
||||
---
|
||||
|
|
|
@ -14,6 +14,12 @@ local rpc_types = {
|
|||
OutputConnect = {
|
||||
response_type = "OutputConnectResponse",
|
||||
},
|
||||
OutputResize = {
|
||||
response_type = "OutputResizeResponse",
|
||||
},
|
||||
OutputMove = {
|
||||
response_type = "OutputMoveResponse",
|
||||
},
|
||||
WindowPointerEnter = {
|
||||
response_type = "WindowPointerEnterResponse",
|
||||
},
|
||||
|
@ -62,6 +68,28 @@ local signals = {
|
|||
---@type fun(response: table)
|
||||
on_response = nil,
|
||||
},
|
||||
OutputResize = {
|
||||
---@nodoc
|
||||
---@type H2Stream?
|
||||
sender = nil,
|
||||
---@nodoc
|
||||
---@type (fun(output: OutputHandle, logical_width: integer, logical_height: integer))[]
|
||||
callbacks = {},
|
||||
---@nodoc
|
||||
---@type fun(response: table)
|
||||
on_response = nil,
|
||||
},
|
||||
OutputMove = {
|
||||
---@nodoc
|
||||
---@type H2Stream?
|
||||
sender = nil,
|
||||
---@nodoc
|
||||
---@type (fun(output: OutputHandle, x: integer, y: integer))[]
|
||||
callbacks = {},
|
||||
---@nodoc
|
||||
---@type fun(response: table)
|
||||
on_response = nil,
|
||||
},
|
||||
WindowPointerEnter = {
|
||||
---@nodoc
|
||||
---@type H2Stream?
|
||||
|
@ -94,6 +122,22 @@ signals.OutputConnect.on_response = function(response)
|
|||
end
|
||||
end
|
||||
|
||||
signals.OutputResize.on_response = function(response)
|
||||
---@diagnostic disable-next-line: invisible
|
||||
local handle = require("pinnacle.output").handle.new(response.output_name)
|
||||
for _, callback in ipairs(signals.OutputResize.callbacks) do
|
||||
callback(handle, response.logical_width, response.logical_height)
|
||||
end
|
||||
end
|
||||
|
||||
signals.OutputMove.on_response = function(response)
|
||||
---@diagnostic disable-next-line: invisible
|
||||
local handle = require("pinnacle.output").handle.new(response.output_name)
|
||||
for _, callback in ipairs(signals.OutputMove.callbacks) do
|
||||
callback(handle, response.x, response.y)
|
||||
end
|
||||
end
|
||||
|
||||
signals.WindowPointerEnter.on_response = function(response)
|
||||
---@diagnostic disable-next-line: invisible
|
||||
local window_handle = require("pinnacle.window").handle.new(response.window_id)
|
||||
|
|
|
@ -17,6 +17,26 @@ message OutputConnectResponse {
|
|||
optional string output_name = 1;
|
||||
}
|
||||
|
||||
message OutputResizeRequest {
|
||||
optional StreamControl control = 1;
|
||||
}
|
||||
// An output's logical size changed
|
||||
message OutputResizeResponse {
|
||||
optional string output_name = 1;
|
||||
optional uint32 logical_width = 2;
|
||||
optional uint32 logical_height = 3;
|
||||
}
|
||||
|
||||
message OutputMoveRequest {
|
||||
optional StreamControl control = 1;
|
||||
}
|
||||
// An output's location in the global space changed
|
||||
message OutputMoveResponse {
|
||||
optional string output_name = 1;
|
||||
optional int32 x = 2;
|
||||
optional int32 y = 3;
|
||||
}
|
||||
|
||||
message WindowPointerEnterRequest {
|
||||
optional StreamControl control = 1;
|
||||
}
|
||||
|
@ -35,6 +55,8 @@ message WindowPointerLeaveResponse {
|
|||
|
||||
service SignalService {
|
||||
rpc OutputConnect(stream OutputConnectRequest) returns (stream OutputConnectResponse);
|
||||
rpc OutputResize(stream OutputResizeRequest) returns (stream OutputResizeResponse);
|
||||
rpc OutputMove(stream OutputMoveRequest) returns (stream OutputMoveResponse);
|
||||
rpc WindowPointerEnter(stream WindowPointerEnterRequest) returns (stream WindowPointerEnterResponse);
|
||||
rpc WindowPointerLeave(stream WindowPointerLeaveRequest) returns (stream WindowPointerLeaveResponse);
|
||||
}
|
||||
|
|
|
@ -159,6 +159,8 @@ impl Output {
|
|||
|
||||
match signal {
|
||||
OutputSignal::Connect(f) => signal_state.output_connect.add_callback(f),
|
||||
OutputSignal::Resize(f) => signal_state.output_resize.add_callback(f),
|
||||
OutputSignal::Move(f) => signal_state.output_move.add_callback(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
//! Some of the other modules have a `connect_signal` method that will allow you to pass in
|
||||
//! callbacks to run on each signal. Use them to connect to the signals defined here.
|
||||
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
sync::{
|
||||
|
@ -138,6 +140,42 @@ signals! {
|
|||
}
|
||||
},
|
||||
}
|
||||
/// An output's logical size changed.
|
||||
///
|
||||
/// Callbacks receive the output and new width and height.
|
||||
OutputResize = {
|
||||
enum_name = Resize,
|
||||
callback_type = Box<dyn FnMut(&OutputHandle, u32, u32) + Send + 'static>,
|
||||
client_request = output_resize,
|
||||
on_response = |response, callbacks| {
|
||||
if let Some(output_name) = &response.output_name {
|
||||
let output = OUTPUT.get().expect("OUTPUT doesn't exist");
|
||||
let handle = output.new_handle(output_name);
|
||||
|
||||
for callback in callbacks {
|
||||
callback(&handle, response.logical_width(), response.logical_height())
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
/// An output's location in the global space changed.
|
||||
///
|
||||
/// Callbacks receive the output and new x and y.
|
||||
OutputMove = {
|
||||
enum_name = Move,
|
||||
callback_type = Box<dyn FnMut(&OutputHandle, i32, i32) + Send + 'static>,
|
||||
client_request = output_move,
|
||||
on_response = |response, callbacks| {
|
||||
if let Some(output_name) = &response.output_name {
|
||||
let output = OUTPUT.get().expect("OUTPUT doesn't exist");
|
||||
let handle = output.new_handle(output_name);
|
||||
|
||||
for callback in callbacks {
|
||||
callback(&handle, response.x(), response.y())
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Signals relating to window events.
|
||||
WindowSignal => {
|
||||
|
@ -185,6 +223,8 @@ pub(crate) type SingleWindowFn = Box<dyn FnMut(&WindowHandle) + Send + 'static>;
|
|||
|
||||
pub(crate) struct SignalState {
|
||||
pub(crate) output_connect: SignalData<OutputConnect>,
|
||||
pub(crate) output_resize: SignalData<OutputResize>,
|
||||
pub(crate) output_move: SignalData<OutputMove>,
|
||||
pub(crate) window_pointer_enter: SignalData<WindowPointerEnter>,
|
||||
pub(crate) window_pointer_leave: SignalData<WindowPointerLeave>,
|
||||
}
|
||||
|
@ -197,6 +237,8 @@ impl SignalState {
|
|||
let client = SignalServiceClient::new(channel);
|
||||
Self {
|
||||
output_connect: SignalData::new(client.clone(), fut_sender.clone()),
|
||||
output_resize: SignalData::new(client.clone(), fut_sender.clone()),
|
||||
output_move: SignalData::new(client.clone(), fut_sender.clone()),
|
||||
window_pointer_enter: SignalData::new(client.clone(), fut_sender.clone()),
|
||||
window_pointer_leave: SignalData::new(client.clone(), fut_sender.clone()),
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ pub mod pinnacle {
|
|||
|
||||
impl_signal_request!(
|
||||
OutputConnectRequest,
|
||||
OutputResizeRequest,
|
||||
OutputMoveRequest,
|
||||
WindowPointerEnterRequest,
|
||||
WindowPointerLeaveRequest
|
||||
);
|
||||
|
|
11
src/api.rs
11
src/api.rs
|
@ -955,8 +955,7 @@ impl output_service_server::OutputService for OutputService {
|
|||
if let Some(y) = y {
|
||||
loc.y = y;
|
||||
}
|
||||
output.change_current_state(None, None, None, Some(loc));
|
||||
state.space.map_output(&output, loc);
|
||||
state.change_output_state(&output, None, None, None, Some(loc));
|
||||
debug!("Mapping output {} to {loc:?}", output.name());
|
||||
state.request_layout(&output);
|
||||
})
|
||||
|
@ -1016,7 +1015,13 @@ impl output_service_server::OutputService for OutputService {
|
|||
|
||||
current_scale = f64::max(current_scale, 0.25);
|
||||
|
||||
output.change_current_state(None, None, Some(Scale::Fractional(current_scale)), None);
|
||||
state.change_output_state(
|
||||
&output,
|
||||
None,
|
||||
None,
|
||||
Some(Scale::Fractional(current_scale)),
|
||||
None,
|
||||
);
|
||||
layer_map_for_output(&output).arrange();
|
||||
state.request_layout(&output);
|
||||
state.schedule_render(&output);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use pinnacle_api_defs::pinnacle::signal::v0alpha1::{
|
||||
signal_service_server, OutputConnectRequest, OutputConnectResponse, SignalRequest,
|
||||
StreamControl, WindowPointerEnterRequest, WindowPointerEnterResponse,
|
||||
WindowPointerLeaveRequest, WindowPointerLeaveResponse,
|
||||
signal_service_server, OutputConnectRequest, OutputConnectResponse, OutputMoveRequest,
|
||||
OutputMoveResponse, OutputResizeRequest, OutputResizeResponse, SignalRequest, StreamControl,
|
||||
WindowPointerEnterRequest, WindowPointerEnterResponse, WindowPointerLeaveRequest,
|
||||
WindowPointerLeaveResponse,
|
||||
};
|
||||
use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle};
|
||||
use tonic::{Request, Response, Status, Streaming};
|
||||
|
@ -15,7 +16,12 @@ use super::{run_bidirectional_streaming, ResponseStream, StateFnSender};
|
|||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SignalState {
|
||||
// Output
|
||||
pub output_connect: SignalData<OutputConnectResponse, VecDeque<OutputConnectResponse>>,
|
||||
pub output_resize: SignalData<OutputResizeResponse, VecDeque<OutputResizeResponse>>,
|
||||
pub output_move: SignalData<OutputMoveResponse, VecDeque<OutputMoveResponse>>,
|
||||
|
||||
// Window
|
||||
pub window_pointer_enter:
|
||||
SignalData<WindowPointerEnterResponse, VecDeque<WindowPointerEnterResponse>>,
|
||||
pub window_pointer_leave:
|
||||
|
@ -171,6 +177,8 @@ impl SignalService {
|
|||
#[tonic::async_trait]
|
||||
impl signal_service_server::SignalService for SignalService {
|
||||
type OutputConnectStream = ResponseStream<OutputConnectResponse>;
|
||||
type OutputResizeStream = ResponseStream<OutputResizeResponse>;
|
||||
type OutputMoveStream = ResponseStream<OutputMoveResponse>;
|
||||
type WindowPointerEnterStream = ResponseStream<WindowPointerEnterResponse>;
|
||||
type WindowPointerLeaveStream = ResponseStream<WindowPointerLeaveResponse>;
|
||||
|
||||
|
@ -185,6 +193,28 @@ impl signal_service_server::SignalService for SignalService {
|
|||
})
|
||||
}
|
||||
|
||||
async fn output_resize(
|
||||
&self,
|
||||
request: Request<Streaming<OutputResizeRequest>>,
|
||||
) -> Result<Response<Self::OutputResizeStream>, Status> {
|
||||
let in_stream = request.into_inner();
|
||||
|
||||
start_signal_stream(self.sender.clone(), in_stream, |state| {
|
||||
&mut state.signal_state.output_resize
|
||||
})
|
||||
}
|
||||
|
||||
async fn output_move(
|
||||
&self,
|
||||
request: Request<Streaming<OutputMoveRequest>>,
|
||||
) -> Result<Response<Self::OutputMoveStream>, Status> {
|
||||
let in_stream = request.into_inner();
|
||||
|
||||
start_signal_stream(self.sender.clone(), in_stream, |state| {
|
||||
&mut state.signal_state.output_move
|
||||
})
|
||||
}
|
||||
|
||||
async fn window_pointer_enter(
|
||||
&self,
|
||||
request: Request<Streaming<WindowPointerEnterRequest>>,
|
||||
|
|
|
@ -272,7 +272,7 @@ impl State {
|
|||
{
|
||||
match render_surface.compositor.use_mode(drm_mode) {
|
||||
Ok(()) => {
|
||||
output.change_current_state(Some(mode), None, None, None);
|
||||
self.change_output_state(output, Some(mode), None, None, None);
|
||||
layer_map_for_output(output).arrange();
|
||||
}
|
||||
Err(err) => error!("Failed to resize output: {err}"),
|
||||
|
@ -280,7 +280,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
output.change_current_state(Some(mode), None, None, None);
|
||||
self.change_output_state(output, Some(mode), None, None, None);
|
||||
layer_map_for_output(output).arrange();
|
||||
}
|
||||
|
||||
|
@ -1042,6 +1042,8 @@ impl State {
|
|||
output.add_mode(mode);
|
||||
}
|
||||
|
||||
output.set_preferred(wl_mode);
|
||||
|
||||
self.output_focus_stack.set_focus(output.clone());
|
||||
|
||||
let x = self.space.outputs().fold(0, |acc, o| {
|
||||
|
@ -1052,10 +1054,6 @@ impl State {
|
|||
});
|
||||
let position = (x, 0).into();
|
||||
|
||||
output.set_preferred(wl_mode);
|
||||
output.change_current_state(Some(wl_mode), None, None, Some(position));
|
||||
self.space.map_output(&output, position);
|
||||
|
||||
output.user_data().insert_if_missing(|| UdevOutputData {
|
||||
crtc,
|
||||
device_id: node,
|
||||
|
@ -1122,6 +1120,8 @@ impl State {
|
|||
|
||||
device.surfaces.insert(crtc, surface);
|
||||
|
||||
self.change_output_state(&output, Some(wl_mode), None, None, Some(position));
|
||||
|
||||
// If there is saved connector state, the connector was previously plugged in.
|
||||
// In this case, restore its tags and location.
|
||||
// TODO: instead of checking the connector, check the monitor's edid info instead
|
||||
|
@ -1131,11 +1131,8 @@ impl State {
|
|||
.get(&OutputName(output.name()))
|
||||
{
|
||||
let ConnectorSavedState { loc, tags, scale } = saved_state;
|
||||
|
||||
output.change_current_state(None, None, *scale, Some(*loc));
|
||||
self.space.map_output(&output, *loc);
|
||||
|
||||
output.with_state_mut(|state| state.tags = tags.clone());
|
||||
self.change_output_state(&output, None, None, *scale, Some(*loc));
|
||||
} else {
|
||||
self.signal_state.output_connect.signal(|buffer| {
|
||||
buffer.push_back(OutputConnectResponse {
|
||||
|
|
|
@ -221,7 +221,8 @@ pub fn setup_winit(
|
|||
size,
|
||||
refresh: 144_000,
|
||||
};
|
||||
output.change_current_state(
|
||||
state.change_output_state(
|
||||
&output,
|
||||
Some(mode),
|
||||
None,
|
||||
Some(Scale::Fractional(scale_factor)),
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use smithay::output::Output;
|
||||
use pinnacle_api_defs::pinnacle::signal::v0alpha1::{OutputMoveResponse, OutputResizeResponse};
|
||||
use smithay::{
|
||||
output::{Mode, Output, Scale},
|
||||
utils::{Logical, Point, Transform},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
focus::WindowKeyboardFocusStack,
|
||||
|
@ -68,3 +72,38 @@ impl OutputState {
|
|||
self.tags.iter().filter(|tag| tag.active())
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// A wrapper around [`Output::change_current_state`] that additionally sends an output
|
||||
/// geometry signal.
|
||||
pub fn change_output_state(
|
||||
&mut self,
|
||||
output: &Output,
|
||||
mode: Option<Mode>,
|
||||
transform: Option<Transform>,
|
||||
scale: Option<Scale>,
|
||||
location: Option<Point<i32, Logical>>,
|
||||
) {
|
||||
output.change_current_state(mode, transform, scale, location);
|
||||
if let Some(location) = location {
|
||||
self.space.map_output(output, location);
|
||||
self.signal_state.output_move.signal(|buf| {
|
||||
buf.push_back(OutputMoveResponse {
|
||||
output_name: Some(output.name()),
|
||||
x: Some(location.x),
|
||||
y: Some(location.y),
|
||||
});
|
||||
});
|
||||
}
|
||||
if mode.is_some() || transform.is_some() || scale.is_some() {
|
||||
self.signal_state.output_resize.signal(|buf| {
|
||||
let geo = self.space.output_geometry(output);
|
||||
buf.push_back(OutputResizeResponse {
|
||||
output_name: Some(output.name()),
|
||||
logical_width: geo.map(|geo| geo.size.w as u32),
|
||||
logical_height: geo.map(|geo| geo.size.h as u32),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue