diff --git a/api/lua/pinnacle/output.lua b/api/lua/pinnacle/output.lua index d118fcd3f..9df106abc 100644 --- a/api/lua/pinnacle/output.lua +++ b/api/lua/pinnacle/output.lua @@ -140,6 +140,8 @@ end local signal_name_to_SignalName = { connect = "OutputConnect", disconnect = "OutputDisconnect", + enable = "OutputEnable", + disable = "OutputDisable", resize = "OutputResize", move = "OutputMove", pointer_enter = "OutputPointerEnter", @@ -148,8 +150,10 @@ local signal_name_to_SignalName = { } ---@class pinnacle.output.OutputSignal Signals related to output events. ----@field connect fun(output: pinnacle.output.OutputHandle)? An output was connected. FIXME: This currently does not fire for outputs that have been previously connected and disconnected. +---@field connect fun(output: pinnacle.output.OutputHandle)? An output was connected for the first time. ---@field disconnect fun(output: pinnacle.output.OutputHandle)? An output was disconnected. +---@field enable fun(output: pinnacle.output.OutputHandle)? An output was enabled. +---@field disable fun(output: pinnacle.output.OutputHandle)? An output was disabled. ---@field resize fun(output: pinnacle.output.OutputHandle, logical_width: integer, logical_height: integer)? An output's logical size changed. ---@field move fun(output: pinnacle.output.OutputHandle, x: integer, y: integer)? An output moved. ---@field pointer_enter fun(output: pinnacle.output.OutputHandle)? The pointer entered an output. diff --git a/api/lua/pinnacle/signal.lua b/api/lua/pinnacle/signal.lua index 1ab85e30d..db73c71af 100644 --- a/api/lua/pinnacle/signal.lua +++ b/api/lua/pinnacle/signal.lua @@ -25,6 +25,22 @@ local signals = { ---@type fun(response: table) on_response = nil, }, + OutputEnable = { + ---@type grpc_client.h2.Stream? + sender = nil, + ---@type { callback_id: integer, callback: fun(output: pinnacle.output.OutputHandle) }[] + callbacks = {}, + ---@type fun(response: table) + on_response = nil, + }, + OutputDisable = { + ---@type grpc_client.h2.Stream? + sender = nil, + ---@type { callback_id: integer, callback: fun(output: pinnacle.output.OutputHandle) }[] + callbacks = {}, + ---@type fun(response: table) + on_response = nil, + }, OutputResize = { ---@type grpc_client.h2.Stream? sender = nil, @@ -169,6 +185,26 @@ signals.OutputDisconnect.on_response = function(response) end end +signals.OutputEnable.on_response = function(response) + ---@diagnostic disable-next-line: invisible + local handle = require("pinnacle.output").handle.new(response.output_name) + local callbacks = require("pinnacle.util").deep_copy(signals.OutputEnable.callbacks) + + for _, callback in ipairs(callbacks) do + protected_callback("OutputEnable", callback.callback, handle) + end +end + +signals.OutputDisable.on_response = function(response) + ---@diagnostic disable-next-line: invisible + local handle = require("pinnacle.output").handle.new(response.output_name) + local callbacks = require("pinnacle.util").deep_copy(signals.OutputDisable.callbacks) + + for _, callback in ipairs(callbacks) do + protected_callback("OutputDisable", callback.callback, handle) + end +end + signals.OutputResize.on_response = function(response) ---@diagnostic disable-next-line: invisible local handle = require("pinnacle.output").handle.new(response.output_name) diff --git a/api/protobuf/pinnacle/signal/v1/signal.proto b/api/protobuf/pinnacle/signal/v1/signal.proto index 74285f5f4..58a52e670 100644 --- a/api/protobuf/pinnacle/signal/v1/signal.proto +++ b/api/protobuf/pinnacle/signal/v1/signal.proto @@ -22,6 +22,18 @@ message OutputDisconnectRequest { message OutputDisconnectResponse { string output_name = 1; } +message OutputEnableRequest { + StreamControl control = 1; +} +message OutputEnableResponse { + string output_name = 1; +} +message OutputDisableRequest { + StreamControl control = 1; +} +message OutputDisableResponse { + string output_name = 1; +} message OutputResizeRequest { StreamControl control = 1; @@ -117,6 +129,8 @@ message InputDeviceAddedResponse { service SignalService { rpc OutputConnect(stream OutputConnectRequest) returns (stream OutputConnectResponse); rpc OutputDisconnect(stream OutputDisconnectRequest) returns (stream OutputDisconnectResponse); + rpc OutputEnable(stream OutputEnableRequest) returns (stream OutputEnableResponse); + rpc OutputDisable(stream OutputDisableRequest) returns (stream OutputDisableResponse); rpc OutputResize(stream OutputResizeRequest) returns (stream OutputResizeResponse); rpc OutputMove(stream OutputMoveRequest) returns (stream OutputMoveResponse); rpc OutputPointerEnter(stream OutputPointerEnterRequest) returns (stream OutputPointerEnterResponse); diff --git a/api/rust/src/output.rs b/api/rust/src/output.rs index bb79300de..c037a255f 100644 --- a/api/rust/src/output.rs +++ b/api/rust/src/output.rs @@ -158,6 +158,8 @@ pub fn connect_signal(signal: OutputSignal) -> SignalHandle { match signal { OutputSignal::Connect(f) => signal_state.output_connect.add_callback(f), OutputSignal::Disconnect(f) => signal_state.output_disconnect.add_callback(f), + OutputSignal::Enable(f) => signal_state.output_enable.add_callback(f), + OutputSignal::Disable(f) => signal_state.output_disable.add_callback(f), OutputSignal::Resize(f) => signal_state.output_resize.add_callback(f), OutputSignal::Move(f) => signal_state.output_move.add_callback(f), OutputSignal::PointerEnter(f) => signal_state.output_pointer_enter.add_callback(f), diff --git a/api/rust/src/signal.rs b/api/rust/src/signal.rs index 3d555be91..5bcf07d20 100644 --- a/api/rust/src/signal.rs +++ b/api/rust/src/signal.rs @@ -122,12 +122,10 @@ macro_rules! signals { signals! { /// Signals relating to output events. OutputSignal => { - /// An output was connected. + /// An output was connected for the first time. + /// This will not trigger if the output was previously connected. /// /// Callbacks receive the newly connected output. - /// - /// FIXME: This will not run on outputs that have been previously connected. - /// | Tell the dev to fix this in the compositor. OutputConnect = { enum_name = Connect, callback_type = SingleOutputFn, @@ -140,7 +138,7 @@ signals! { } }, } - /// An output was connected. + /// An output was disconnected. /// /// Callbacks receive the disconnected output. OutputDisconnect = { @@ -155,6 +153,36 @@ signals! { } }, } + /// An output was enabled. + /// + /// Callbacks receive the enabled output. + OutputEnable = { + enum_name = Enable, + callback_type = SingleOutputFn, + client_request = output_enable, + on_response = |response, callbacks| { + let handle = OutputHandle { name: response.output_name }; + + for callback in callbacks { + callback(&handle); + } + }, + } + /// An output was disabled. + /// + /// Callbacks receive the disabled output. + OutputDisable = { + enum_name = Disable, + callback_type = SingleOutputFn, + client_request = output_disable, + on_response = |response, callbacks| { + let handle = OutputHandle { name: response.output_name }; + + for callback in callbacks { + callback(&handle); + } + }, + } /// An output's logical size changed. /// /// Callbacks receive the output and new width and height. @@ -335,6 +363,8 @@ pub(crate) type SingleWindowFn = Box; pub(crate) struct SignalState { pub(crate) output_connect: SignalData, pub(crate) output_disconnect: SignalData, + pub(crate) output_enable: SignalData, + pub(crate) output_disable: SignalData, pub(crate) output_resize: SignalData, pub(crate) output_move: SignalData, pub(crate) output_pointer_enter: SignalData, @@ -362,6 +392,8 @@ impl SignalState { Self { output_connect: SignalData::new(), output_disconnect: SignalData::new(), + output_enable: SignalData::new(), + output_disable: SignalData::new(), output_resize: SignalData::new(), output_move: SignalData::new(), output_pointer_enter: SignalData::new(), @@ -382,6 +414,8 @@ impl SignalState { pub(crate) fn shutdown(&mut self) { self.output_connect.reset(); self.output_disconnect.reset(); + self.output_enable.reset(); + self.output_disable.reset(); self.output_resize.reset(); self.output_move.reset(); self.output_pointer_enter.reset(); diff --git a/pinnacle-api-defs/src/lib.rs b/pinnacle-api-defs/src/lib.rs index f04dd835d..ac7cd2c66 100644 --- a/pinnacle-api-defs/src/lib.rs +++ b/pinnacle-api-defs/src/lib.rs @@ -64,6 +64,8 @@ pub mod pinnacle { impl_signal_request!( OutputConnectRequest, OutputDisconnectRequest, + OutputEnableRequest, + OutputDisableRequest, OutputResizeRequest, OutputMoveRequest, OutputPointerEnterRequest, diff --git a/src/api/signal.rs b/src/api/signal.rs index ec78b5d83..9608f93a6 100644 --- a/src/api/signal.rs +++ b/src/api/signal.rs @@ -7,14 +7,15 @@ use pinnacle_api_defs::pinnacle::signal::{ self, v1::{ InputDeviceAddedRequest, InputDeviceAddedResponse, OutputConnectRequest, - OutputConnectResponse, OutputDisconnectRequest, OutputDisconnectResponse, - OutputFocusedRequest, OutputFocusedResponse, OutputMoveRequest, OutputMoveResponse, - OutputPointerEnterRequest, OutputPointerEnterResponse, OutputPointerLeaveRequest, - OutputPointerLeaveResponse, OutputResizeRequest, OutputResizeResponse, SignalRequest, - StreamControl, TagActiveRequest, TagActiveResponse, WindowFocusedRequest, - WindowFocusedResponse, WindowPointerEnterRequest, WindowPointerEnterResponse, - WindowPointerLeaveRequest, WindowPointerLeaveResponse, WindowTitleChangedRequest, - WindowTitleChangedResponse, + OutputConnectResponse, OutputDisableRequest, OutputDisableResponse, + OutputDisconnectRequest, OutputDisconnectResponse, OutputEnableRequest, + OutputEnableResponse, OutputFocusedRequest, OutputFocusedResponse, OutputMoveRequest, + OutputMoveResponse, OutputPointerEnterRequest, OutputPointerEnterResponse, + OutputPointerLeaveRequest, OutputPointerLeaveResponse, OutputResizeRequest, + OutputResizeResponse, SignalRequest, StreamControl, TagActiveRequest, TagActiveResponse, + WindowFocusedRequest, WindowFocusedResponse, WindowPointerEnterRequest, + WindowPointerEnterResponse, WindowPointerLeaveRequest, WindowPointerLeaveResponse, + WindowTitleChangedRequest, WindowTitleChangedResponse, }, }; use smithay::output::Output; @@ -35,6 +36,8 @@ pub struct SignalState { // Output pub output_connect: OutputConnect, pub output_disconnect: OutputDisconnect, + pub output_enable: OutputEnable, + pub output_disable: OutputDisable, pub output_resize: OutputResize, pub output_move: OutputMove, pub output_pointer_enter: OutputPointerEnter, @@ -136,6 +139,48 @@ impl Signal for OutputDisconnect { } } +#[derive(Debug, Default)] +pub struct OutputEnable { + v1: SignalData, +} + +impl Signal for OutputEnable { + type Args<'a> = &'a smithay::output::Output; + + fn signal(&mut self, args: Self::Args<'_>) { + self.v1.signal(|buf| { + buf.push_back(signal::v1::OutputEnableResponse { + output_name: args.name(), + }); + }); + } + + fn clear(&mut self) { + self.v1.instances.clear(); + } +} + +#[derive(Debug, Default)] +pub struct OutputDisable { + v1: SignalData, +} + +impl Signal for OutputDisable { + type Args<'a> = &'a smithay::output::Output; + + fn signal(&mut self, args: Self::Args<'_>) { + self.v1.signal(|buf| { + buf.push_back(signal::v1::OutputDisableResponse { + output_name: args.name(), + }); + }); + } + + fn clear(&mut self) { + self.v1.instances.clear(); + } +} + #[derive(Debug, Default)] pub struct OutputResize { v1: SignalData, @@ -482,6 +527,8 @@ impl SignalService { impl signal::v1::signal_service_server::SignalService for SignalService { type OutputConnectStream = ResponseStream; type OutputDisconnectStream = ResponseStream; + type OutputEnableStream = ResponseStream; + type OutputDisableStream = ResponseStream; type OutputResizeStream = ResponseStream; type OutputMoveStream = ResponseStream; type OutputPointerEnterStream = ResponseStream; @@ -519,6 +566,28 @@ impl signal::v1::signal_service_server::SignalService for SignalService { }) } + async fn output_enable( + &self, + request: Request>, + ) -> Result, Status> { + let in_stream = request.into_inner(); + + start_signal_stream(self.sender.clone(), in_stream, |state| { + &mut state.pinnacle.signal_state.output_enable.v1 + }) + } + + async fn output_disable( + &self, + request: Request>, + ) -> Result, Status> { + let in_stream = request.into_inner(); + + start_signal_stream(self.sender.clone(), in_stream, |state| { + &mut state.pinnacle.signal_state.output_disable.v1 + }) + } + async fn output_resize( &self, request: Request>, diff --git a/src/backend.rs b/src/backend.rs index 962a91a9a..c6f1e8556 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -18,6 +18,7 @@ use tracing::{error, warn}; use wayland_backend::server::GlobalId; use crate::{ + api::signal::Signal, output::OutputMode, state::{Pinnacle, State, WithState}, }; @@ -187,6 +188,12 @@ impl State { self.pinnacle .output_power_management_state .mode_set(output, powered); + + if powered { + self.pinnacle.signal_state.output_enable.signal(output); + } else { + self.pinnacle.signal_state.output_disable.signal(output); + } } } diff --git a/src/output.rs b/src/output.rs index 2471ff2f8..a400369ba 100644 --- a/src/output.rs +++ b/src/output.rs @@ -179,6 +179,10 @@ impl Pinnacle { ) { let _span = tracy_client::span!("Pinnacle::change_output_state"); + if let Some(_mode) = mode { + self.set_output_enabled(output, true); + } + // Calculate the ratio that the pointer location was over the output's size // so we can warp it if the output moves let pointer_loc_ratio = self.seat.get_pointer().and_then(|ptr| { @@ -308,23 +312,19 @@ impl Pinnacle { self.space.map_output(output, output.current_location()); // Trigger the connect signal here for configs to reposition outputs - // - // TODO: Create a new output_disable/enable signal and trigger it here - // instead of connect and disconnect if should_signal { self.signal_state.output_connect.signal(output); } + + self.signal_state.output_enable.signal(output); } else { if let Some(global) = output.with_state_mut(|state| state.enabled_global_id.take()) { self.display_handle.remove_global::(global); } self.space.unmap_output(output); - // Trigger the disconnect signal here for configs to reposition outputs - // - // TODO: Create a new output_disable/enable signal and trigger it here - // instead of connect and disconnect - self.signal_state.output_disconnect.signal(output); + // Trigger the disable signal here for configs to reposition outputs + self.signal_state.output_disable.signal(output); self.gamma_control_manager_state.output_removed(output);