Add tag active signal

This commit is contained in:
Ottatop 2024-04-18 14:42:49 -05:00
parent c0aa9067f3
commit 1c2f639a56
9 changed files with 171 additions and 13 deletions

View file

@ -23,12 +23,17 @@ local rpc_types = {
OutputMove = {
response_type = "OutputMoveResponse",
},
WindowPointerEnter = {
response_type = "WindowPointerEnterResponse",
},
WindowPointerLeave = {
response_type = "WindowPointerLeaveResponse",
},
TagActive = {
response_type = "TagActiveResponse",
},
}
---Build GrpcRequestParams
@ -126,6 +131,17 @@ local signals = {
---@type fun(response: table)
on_response = nil,
},
TagActive = {
---@nodoc
---@type H2Stream?
sender = nil,
---@nodoc
---@type (fun(tag: TagHandle, active: boolean))[]
callbacks = {},
---@nodoc
---@type fun(response: table)
on_response = nil,
},
}
signals.OutputConnect.on_response = function(response)
@ -178,6 +194,15 @@ signals.WindowPointerLeave.on_response = function(response)
end
end
signals.TagActive.on_response = function(response)
---@diagnostic disable-next-line: invisible
local tag_handle = require("pinnacle.tag").handle.new(response.tag_id)
for _, callback in ipairs(signals.TagActive.callbacks) do
callback(tag_handle, response.active)
end
end
-----------------------------------------------------------------------------
---@nodoc

View file

@ -210,6 +210,52 @@ function tag.remove(tags)
client.unary_request(build_grpc_request_params("Remove", { tag_ids = ids }))
end
---@type table<string, SignalServiceMethod>
local signal_name_to_SignalName = {
active = "TagActive",
}
---@class TagSignal Signals related to tag events.
---@field active fun(tag: TagHandle, active: boolean)? A tag was set to active or not active.
---Connect to a tag signal.
---
---The compositor sends signals about various events. Use this function to run a callback when
---some tag signal occurs.
---
---This function returns a table of signal handles with each handle stored at the same key used
---to connect to the signal. See `SignalHandles` for more information.
---
---# Example
---```lua
---Tag.connect_signal({
--- active = function(tag, active)
--- print("Activity for " .. tag:name() .. " was set to", active)
--- end
---})
---```
---
---@param signals TagSignal The signal you want to connect to
---
---@return SignalHandles signal_handles Handles to every signal you connected to wrapped in a table, with keys being the same as the connected signal.
---
---@see SignalHandles.disconnect_all - To disconnect from these signals
function tag.connect_signal(signals)
---@diagnostic disable-next-line: invisible
local handles = require("pinnacle.signal").handles.new({})
for signal, callback in pairs(signals) do
require("pinnacle.signal").add_callback(signal_name_to_SignalName[signal], callback)
---@diagnostic disable-next-line: invisible
local handle = require("pinnacle.signal").handle.new(signal_name_to_SignalName[signal], callback)
handles[signal] = handle
end
return handles
end
--------------------------------------------------------------
---Remove this tag.
---
---### Example

View file

@ -26,6 +26,7 @@ message OutputDisconnectResponse {
message OutputResizeRequest {
optional StreamControl control = 1;
}
// An output's logical size changed
message OutputResizeResponse {
optional string output_name = 1;
@ -36,6 +37,7 @@ message OutputResizeResponse {
message OutputMoveRequest {
optional StreamControl control = 1;
}
// An output's location in the global space changed
message OutputMoveResponse {
optional string output_name = 1;
@ -59,11 +61,23 @@ message WindowPointerLeaveResponse {
optional uint32 window_id = 1;
}
message TagActiveRequest {
optional StreamControl control = 1;
}
message TagActiveResponse {
optional uint32 tag_id = 1;
// The tag was set to active or inactive.
optional bool active = 2;
}
service SignalService {
rpc OutputConnect(stream OutputConnectRequest) returns (stream OutputConnectResponse);
rpc OutputDisconnect(stream OutputDisconnectRequest) returns (stream OutputDisconnectResponse);
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);
rpc TagActive(stream TagActiveRequest) returns (stream TagActiveResponse);
}

View file

@ -27,7 +27,9 @@ use tokio::sync::{
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use tonic::{transport::Channel, Streaming};
use crate::{block_on_tokio, output::OutputHandle, window::WindowHandle, ApiModules};
use crate::{
block_on_tokio, output::OutputHandle, tag::TagHandle, window::WindowHandle, ApiModules,
};
pub(crate) trait Signal {
type Callback;
@ -229,6 +231,24 @@ signals! {
},
}
}
/// Signals relating to tag events.
TagSignal => {
/// A tag was set to active or not active.
TagActive = {
enum_name = Active,
callback_type = Box<dyn FnMut(&TagHandle, bool) + Send + 'static>,
client_request = tag_active,
on_response = |response, callbacks, api| {
if let Some(tag_id) = response.tag_id {
let handle = api.tag.new_handle(tag_id);
for callback in callbacks {
callback(&handle, response.active.unwrap());
}
}
},
}
}
}
pub(crate) type SingleOutputFn = Box<dyn FnMut(&OutputHandle) + Send + 'static>;
@ -239,8 +259,11 @@ pub(crate) struct SignalState {
pub(crate) output_disconnect: SignalData<OutputDisconnect>,
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>,
pub(crate) tag_active: SignalData<TagActive>,
}
impl std::fmt::Debug for SignalState {
@ -262,6 +285,7 @@ impl SignalState {
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()),
tag_active: SignalData::new(client.clone(), fut_sender.clone()),
}
}
@ -272,6 +296,7 @@ impl SignalState {
self.output_move.api.set(api.clone()).unwrap();
self.window_pointer_enter.api.set(api.clone()).unwrap();
self.window_pointer_leave.api.set(api.clone()).unwrap();
self.tag_active.api.set(api.clone()).unwrap();
}
}

View file

@ -44,7 +44,14 @@ use pinnacle_api_defs::pinnacle::{
};
use tonic::transport::Channel;
use crate::{block_on_tokio, output::OutputHandle, util::Batch, window::WindowHandle, ApiModules};
use crate::{
block_on_tokio,
output::OutputHandle,
signal::{SignalHandle, TagSignal},
util::Batch,
window::WindowHandle,
ApiModules,
};
/// A struct that allows you to add and remove tags and get [`TagHandle`]s.
#[derive(Clone, Debug)]
@ -227,6 +234,19 @@ impl Tag {
block_on_tokio(client.remove(RemoveRequest { tag_ids })).unwrap();
}
/// Connect to a tag signal.
///
/// The compositor will fire off signals that your config can listen for and act upon.
/// You can pass in a [`TagSignal`] along with a callback and it will get run
/// with the necessary arguments every time a signal of that type is received.
pub fn connect_signal(&self, signal: TagSignal) -> SignalHandle {
let mut signal_state = block_on_tokio(self.api.get().unwrap().signal.write());
match signal {
TagSignal::Active(f) => signal_state.tag_active.add_callback(f),
}
}
}
/// A handle to a tag.

View file

@ -67,7 +67,8 @@ pub mod pinnacle {
OutputResizeRequest,
OutputMoveRequest,
WindowPointerEnterRequest,
WindowPointerLeaveRequest
WindowPointerLeaveRequest,
TagActiveRequest
);
}
}

View file

@ -706,9 +706,9 @@ impl tag_service_server::TagService for TagService {
};
match set_or_toggle {
SetOrToggle::Set => tag.set_active(true),
SetOrToggle::Unset => tag.set_active(false),
SetOrToggle::Toggle => tag.set_active(!tag.active()),
SetOrToggle::Set => tag.set_active(true, state),
SetOrToggle::Unset => tag.set_active(false, state),
SetOrToggle::Toggle => tag.set_active(!tag.active(), state),
SetOrToggle::Unspecified => unreachable!(),
}
@ -736,11 +736,11 @@ impl tag_service_server::TagService for TagService {
let Some(tag) = tag_id.tag(state) else { return };
let Some(output) = tag.output(state) else { return };
output.with_state_mut(|state| {
for op_tag in state.tags.iter_mut() {
op_tag.set_active(false);
output.with_state_mut(|op_state| {
for op_tag in op_state.tags.iter_mut() {
op_tag.set_active(false, state);
}
tag.set_active(true);
tag.set_active(true, state);
});
state.request_layout(&output);

View file

@ -3,8 +3,9 @@ use std::collections::VecDeque;
use pinnacle_api_defs::pinnacle::signal::v0alpha1::{
signal_service_server, OutputConnectRequest, OutputConnectResponse, OutputDisconnectRequest,
OutputDisconnectResponse, OutputMoveRequest, OutputMoveResponse, OutputResizeRequest,
OutputResizeResponse, SignalRequest, StreamControl, WindowPointerEnterRequest,
WindowPointerEnterResponse, WindowPointerLeaveRequest, WindowPointerLeaveResponse,
OutputResizeResponse, SignalRequest, StreamControl, TagActiveRequest, TagActiveResponse,
WindowPointerEnterRequest, WindowPointerEnterResponse, WindowPointerLeaveRequest,
WindowPointerLeaveResponse,
};
use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle};
use tonic::{Request, Response, Status, Streaming};
@ -27,6 +28,9 @@ pub struct SignalState {
SignalData<WindowPointerEnterResponse, VecDeque<WindowPointerEnterResponse>>,
pub window_pointer_leave:
SignalData<WindowPointerLeaveResponse, VecDeque<WindowPointerLeaveResponse>>,
// Tag
pub tag_active: SignalData<TagActiveResponse, VecDeque<TagActiveResponse>>,
}
impl SignalState {
@ -184,9 +188,12 @@ impl signal_service_server::SignalService for SignalService {
type OutputDisconnectStream = ResponseStream<OutputDisconnectResponse>;
type OutputResizeStream = ResponseStream<OutputResizeResponse>;
type OutputMoveStream = ResponseStream<OutputMoveResponse>;
type WindowPointerEnterStream = ResponseStream<WindowPointerEnterResponse>;
type WindowPointerLeaveStream = ResponseStream<WindowPointerLeaveResponse>;
type TagActiveStream = ResponseStream<TagActiveResponse>;
async fn output_connect(
&self,
request: Request<Streaming<OutputConnectRequest>>,
@ -252,4 +259,15 @@ impl signal_service_server::SignalService for SignalService {
&mut state.signal_state.window_pointer_leave
})
}
async fn tag_active(
&self,
request: Request<Streaming<TagActiveRequest>>,
) -> Result<Response<Self::TagActiveStream>, Status> {
let in_stream = request.into_inner();
start_signal_stream(self.sender.clone(), in_stream, |state| {
&mut state.signal_state.tag_active
})
}
}

View file

@ -87,8 +87,17 @@ impl Tag {
self.0.lock().expect("tag already locked").active
}
pub fn set_active(&self, active: bool) {
pub fn set_active(&self, active: bool, state: &mut State) {
self.0.lock().expect("tag already locked").active = active;
state.signal_state.tag_active.signal(|buf| {
buf.push_back(
pinnacle_api_defs::pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(self.id().0),
active: Some(self.active()),
},
);
})
}
}