From 286a8aebfe95889cd1d3c4c8556a3c6e1abbbe7a Mon Sep 17 00:00:00 2001 From: phantomas Date: Mon, 30 Mar 2026 01:42:16 +0200 Subject: [PATCH 1/3] snowcap: Properly handle close events for surfaces --- .../snowcap/decoration/v1/decoration.proto | 15 ++++++ .../api/protobuf/snowcap/layer/v1/layer.proto | 3 ++ .../api/protobuf/snowcap/popup/v1/popup.proto | 3 ++ snowcap/src/api/decoration/v1.rs | 50 +++++++++++++++++-- snowcap/src/api/layer/v1.rs | 11 ++-- snowcap/src/api/popup/v1.rs | 11 ++-- snowcap/src/decoration.rs | 12 +++++ snowcap/src/layer.rs | 9 ++++ snowcap/src/popup.rs | 9 ++++ 9 files changed, 108 insertions(+), 15 deletions(-) diff --git a/snowcap/api/protobuf/snowcap/decoration/v1/decoration.proto b/snowcap/api/protobuf/snowcap/decoration/v1/decoration.proto index 645322866..d520dcc98 100644 --- a/snowcap/api/protobuf/snowcap/decoration/v1/decoration.proto +++ b/snowcap/api/protobuf/snowcap/decoration/v1/decoration.proto @@ -48,10 +48,25 @@ message ViewRequest { } message ViewResponse {} +message GetDecorationEventsRequest { + uint32 decoration_id = 1; +} +message DecorationEvent { + message Closing {} + + oneof event { + Closing closing = 1; + } +} +message GetDecorationEventsResponse { + repeated DecorationEvent decoration_events = 1; +} + service DecorationService { rpc NewDecoration(NewDecorationRequest) returns (NewDecorationResponse); rpc Close(CloseRequest) returns (CloseResponse); rpc OperateDecoration(OperateDecorationRequest) returns (OperateDecorationResponse); rpc UpdateDecoration(UpdateDecorationRequest) returns (UpdateDecorationResponse); rpc RequestView(ViewRequest) returns (ViewResponse); + rpc GetDecorationEvents(GetDecorationEventsRequest) returns (stream GetDecorationEventsResponse); } diff --git a/snowcap/api/protobuf/snowcap/layer/v1/layer.proto b/snowcap/api/protobuf/snowcap/layer/v1/layer.proto index 1a47091b0..54167641f 100644 --- a/snowcap/api/protobuf/snowcap/layer/v1/layer.proto +++ b/snowcap/api/protobuf/snowcap/layer/v1/layer.proto @@ -82,8 +82,11 @@ message LayerEvent { FOCUS_LOST = 2; } + message Closing {} + oneof event { Focus focus = 1; + Closing closing = 2; } } diff --git a/snowcap/api/protobuf/snowcap/popup/v1/popup.proto b/snowcap/api/protobuf/snowcap/popup/v1/popup.proto index ea168dbc3..85d15c615 100644 --- a/snowcap/api/protobuf/snowcap/popup/v1/popup.proto +++ b/snowcap/api/protobuf/snowcap/popup/v1/popup.proto @@ -119,8 +119,11 @@ message PopupEvent { FOCUS_LOST = 2; } + message Closing {} + oneof event { Focus focus = 1; + Closing closing = 2; } } diff --git a/snowcap/src/api/decoration/v1.rs b/snowcap/src/api/decoration/v1.rs index 6bf837ae6..dddd74918 100644 --- a/snowcap/src/api/decoration/v1.rs +++ b/snowcap/src/api/decoration/v1.rs @@ -1,20 +1,23 @@ use anyhow::Context; use snowcap_api_defs::snowcap::decoration::v1::{ - CloseRequest, CloseResponse, NewDecorationRequest, NewDecorationResponse, - OperateDecorationRequest, OperateDecorationResponse, UpdateDecorationRequest, - UpdateDecorationResponse, ViewRequest, ViewResponse, decoration_service_server, + CloseRequest, CloseResponse, GetDecorationEventsRequest, GetDecorationEventsResponse, + NewDecorationRequest, NewDecorationResponse, OperateDecorationRequest, + OperateDecorationResponse, UpdateDecorationRequest, UpdateDecorationResponse, ViewRequest, + ViewResponse, decoration_service_server, }; use tonic::{Request, Response, Status}; use tracing::warn; use crate::{ - api::{run_unary, widget::v1::widget_def_to_fn}, - decoration::{DecorationId, SnowcapDecoration}, + api::{ResponseStream, run_server_streaming_mapped, run_unary, widget::v1::widget_def_to_fn}, + decoration::{DecorationEvent, DecorationId, SnowcapDecoration}, util::convert::TryFromApi, }; #[tonic::async_trait] impl decoration_service_server::DecorationService for super::DecorationService { + type GetDecorationEventsStream = ResponseStream; + async fn new_decoration( &self, request: Request, @@ -169,6 +172,29 @@ impl decoration_service_server::DecorationService for super::DecorationService { .await } + async fn get_decoration_events( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + + let id = request.decoration_id; + + run_server_streaming_mapped( + &self.sender, + move |state, sender| { + if let Some(decoration) = state.decoration_for_id(DecorationId(id)) { + decoration.decoration_event_sender = Some(sender); + } + }, + move |events| { + Ok(GetDecorationEventsResponse { + decoration_events: events.into_iter().map(Into::into).collect(), + }) + }, + ) + } + async fn request_view( &self, request: Request, @@ -195,3 +221,17 @@ impl decoration_service_server::DecorationService for super::DecorationService { .await } } + +impl From for snowcap_api_defs::snowcap::decoration::v1::DecorationEvent { + fn from(value: DecorationEvent) -> Self { + use snowcap_api_defs::snowcap::decoration::v1::decoration_event; + + match value { + DecorationEvent::Closing => Self { + event: Some(decoration_event::Event::Closing( + decoration_event::Closing {}, + )), + }, + } + } +} diff --git a/snowcap/src/api/layer/v1.rs b/snowcap/src/api/layer/v1.rs index a921932aa..601ede5e6 100644 --- a/snowcap/src/api/layer/v1.rs +++ b/snowcap/src/api/layer/v1.rs @@ -273,15 +273,16 @@ impl From for snowcap_api_defs::snowcap::layer::v1::LayerEvent { use crate::handlers::keyboard::KeyboardFocusEvent; use snowcap_api_defs::snowcap::layer::v1::layer_event::{self, Focus}; - let LayerEvent::Focus(f) = value; - - match f { - KeyboardFocusEvent::FocusGained => Self { + match value { + LayerEvent::Focus(KeyboardFocusEvent::FocusGained) => Self { event: Some(layer_event::Event::Focus(Focus::Gained.into())), }, - KeyboardFocusEvent::FocusLost => Self { + LayerEvent::Focus(KeyboardFocusEvent::FocusLost) => Self { event: Some(layer_event::Event::Focus(Focus::Lost.into())), }, + LayerEvent::Closing => Self { + event: Some(layer_event::Event::Closing(layer_event::Closing {})), + }, } } } diff --git a/snowcap/src/api/popup/v1.rs b/snowcap/src/api/popup/v1.rs index d299f8e53..f63eccc77 100644 --- a/snowcap/src/api/popup/v1.rs +++ b/snowcap/src/api/popup/v1.rs @@ -455,15 +455,16 @@ impl From for snowcap_api_defs::snowcap::popup::v1::PopupEvent { use crate::handlers::keyboard::KeyboardFocusEvent; use snowcap_api_defs::snowcap::popup::v1::popup_event::{self, Focus}; - let PopupEvent::Focus(f) = value; - - match f { - KeyboardFocusEvent::FocusGained => Self { + match value { + PopupEvent::Focus(KeyboardFocusEvent::FocusGained) => Self { event: Some(popup_event::Event::Focus(Focus::Gained.into())), }, - KeyboardFocusEvent::FocusLost => Self { + PopupEvent::Focus(KeyboardFocusEvent::FocusLost) => Self { event: Some(popup_event::Event::Focus(Focus::Lost.into())), }, + PopupEvent::Closing => Self { + event: Some(popup_event::Event::Closing(popup_event::Closing {})), + }, } } } diff --git a/snowcap/src/decoration.rs b/snowcap/src/decoration.rs index 68d9bc5d1..9938b30e2 100644 --- a/snowcap/src/decoration.rs +++ b/snowcap/src/decoration.rs @@ -3,6 +3,7 @@ use smithay_client_toolkit::reexports::{ protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, }; use snowcap_protocols::snowcap_decoration_v1::client::snowcap_decoration_surface_v1::SnowcapDecorationSurfaceV1; +use tokio::sync::mpsc::UnboundedSender; use crate::{popup::ParentId, state::State, surface::SnowcapSurface, widget::ViewFn}; @@ -21,6 +22,11 @@ impl DecorationIdCounter { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DecorationEvent { + Closing, +} + impl State { pub fn decoration_for_id(&mut self, id: DecorationId) -> Option<&mut SnowcapDecoration> { self.decorations @@ -66,11 +72,16 @@ pub struct SnowcapDecoration { bounds: Bounds, pending_bounds: Option, pending_z_index: Option, + + pub decoration_event_sender: Option>>, } impl Drop for SnowcapDecoration { fn drop(&mut self) { self.decoration.destroy(); + if let Some(sender) = self.decoration_event_sender.as_ref() { + let _ = sender.send(vec![DecorationEvent::Closing]); + } } } @@ -146,6 +157,7 @@ impl SnowcapDecoration { bounds, pending_bounds: None, pending_z_index: None, + decoration_event_sender: None, }) } diff --git a/snowcap/src/layer.rs b/snowcap/src/layer.rs index 86a7c6a30..8208bc593 100644 --- a/snowcap/src/layer.rs +++ b/snowcap/src/layer.rs @@ -38,6 +38,7 @@ impl LayerIdCounter { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LayerEvent { Focus(KeyboardFocusEvent), + Closing, } impl State { @@ -260,6 +261,14 @@ impl SnowcapLayer { } } +impl Drop for SnowcapLayer { + fn drop(&mut self) { + if let Some(sender) = self.layer_event_sender.as_ref() { + let _ = sender.send(vec![LayerEvent::Closing]); + } + } +} + impl From for LayerEvent { fn from(value: KeyboardFocusEvent) -> Self { Self::Focus(value) diff --git a/snowcap/src/popup.rs b/snowcap/src/popup.rs index 2795862f6..c51fc0569 100644 --- a/snowcap/src/popup.rs +++ b/snowcap/src/popup.rs @@ -37,6 +37,7 @@ impl PopupIdCounter { pub enum PopupEvent { Focus(KeyboardFocusEvent), + Closing, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] @@ -629,6 +630,14 @@ impl SnowcapPopup { } } +impl Drop for SnowcapPopup { + fn drop(&mut self) { + if let Some(sender) = self.popup_event_sender.as_ref() { + let _ = sender.send(vec![PopupEvent::Closing]); + } + } +} + impl From for PopupEvent { fn from(value: KeyboardFocusEvent) -> Self { Self::Focus(value) From 40a7266eecce013ef0005b4ab6e1d28832ccab7a Mon Sep 17 00:00:00 2001 From: phantomas Date: Mon, 30 Mar 2026 01:42:54 +0200 Subject: [PATCH 2/3] snowcap-api: rust: Properly handle close events for surfaces --- snowcap/api/rust/src/surface/decoration.rs | 22 +++++++++++++++++----- snowcap/api/rust/src/surface/layer.rs | 12 +++++++++--- snowcap/api/rust/src/surface/popup.rs | 12 +++++++++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/snowcap/api/rust/src/surface/decoration.rs b/snowcap/api/rust/src/surface/decoration.rs index 07670ece2..bc6df3c1d 100644 --- a/snowcap/api/rust/src/surface/decoration.rs +++ b/snowcap/api/rust/src/surface/decoration.rs @@ -6,8 +6,8 @@ use snowcap_api_defs::snowcap::{ decoration::{ self, v1::{ - CloseRequest, NewDecorationRequest, OperateDecorationRequest, UpdateDecorationRequest, - ViewRequest, + CloseRequest, GetDecorationEventsRequest, NewDecorationRequest, + OperateDecorationRequest, UpdateDecorationRequest, ViewRequest, decoration_event, }, }, widget::v1::{GetWidgetEventsRequest, get_widget_events_request}, @@ -100,13 +100,18 @@ where let decoration_id = response.into_inner().decoration_id; - let mut event_stream = Client::widget() + let mut widget_event_stream = Client::widget() .get_widget_events(GetWidgetEventsRequest { id: Some(get_widget_events_request::Id::DecorationId(decoration_id)), }) .block_on_tokio()? .into_inner(); + let mut decoration_event_stream = Client::decoration() + .get_decoration_events(GetDecorationEventsRequest { decoration_id }) + .block_on_tokio()? + .into_inner(); + let (msg_send, mut msg_recv) = tokio::sync::mpsc::unbounded_channel::>(); let handle = DecorationHandle { @@ -165,9 +170,9 @@ where }); tokio::spawn(async move { - loop { + 'main_loop: loop { tokio::select! { - Some(Ok(response)) = event_stream.next() => { + Some(Ok(response)) = widget_event_stream.next() => { for widget_event in response.widget_events { let Some(msg) = widget::message_from_event(&callbacks, widget_event) else { continue; @@ -190,6 +195,13 @@ where continue; } + Some(Ok(response)) = decoration_event_stream.next() => { + for decoration_event in response.decoration_events { + if matches!(decoration_event.event, Some(decoration_event::Event::Closing(_))) { + break 'main_loop; + } + } + } else => break, }; diff --git a/snowcap/api/rust/src/surface/layer.rs b/snowcap/api/rust/src/surface/layer.rs index f25c7c66a..0eceecbfb 100644 --- a/snowcap/api/rust/src/surface/layer.rs +++ b/snowcap/api/rust/src/surface/layer.rs @@ -8,7 +8,7 @@ use snowcap_api_defs::snowcap::{ self, v1::{ CloseRequest, GetLayerEventsRequest, NewLayerRequest, OperateLayerRequest, - UpdateLayerRequest, ViewRequest, + UpdateLayerRequest, ViewRequest, layer_event, }, }, widget::v1::{GetWidgetEventsRequest, get_widget_events_request}, @@ -136,7 +136,9 @@ impl TryFrom for SurfaceEvent { fn try_from(value: layer::v1::layer_event::Event) -> Result { use layer::v1::layer_event::{Event, Focus}; - let Event::Focus(f) = value; + let Event::Focus(f) = value else { + return Err(LayerEventError::Unknown); + }; match Focus::try_from(f) { Ok(Focus::Gained) => Ok(Self::FocusGained), @@ -268,7 +270,7 @@ where }); tokio::spawn(async move { - loop { + 'main_loop: loop { tokio::select! { Some(Ok(response)) = widget_event_stream.next() => { for widget_event in response.widget_events { @@ -295,6 +297,10 @@ where } Some(Ok(response)) = layer_event_stream.next() => { for layer_event in response.layer_events { + if matches!(layer_event.event, Some(layer_event::Event::Closing(_))) { + break 'main_loop; + } + let Some(event) = layer_event .event .and_then(|e| e.try_into().ok()) else { diff --git a/snowcap/api/rust/src/surface/popup.rs b/snowcap/api/rust/src/surface/popup.rs index 26868e1e2..dd4ca2b27 100644 --- a/snowcap/api/rust/src/surface/popup.rs +++ b/snowcap/api/rust/src/surface/popup.rs @@ -9,7 +9,7 @@ use snowcap_api_defs::snowcap::{ self, v1::{ CloseRequest, GetPopupEventsRequest, NewPopupRequest, OperatePopupRequest, - UpdatePopupRequest, ViewRequest, + UpdatePopupRequest, ViewRequest, popup_event, }, }, widget::v1::{GetWidgetEventsRequest, get_widget_events_request}, @@ -162,7 +162,9 @@ impl TryFrom for SurfaceEvent { fn try_from(value: popup::v1::popup_event::Event) -> Result { use popup::v1::popup_event::{Event, Focus}; - let Event::Focus(f) = value; + let Event::Focus(f) = value else { + return Err(PopupEventError::Unknown); + }; match Focus::try_from(f) { Ok(Focus::Gained) => Ok(Self::FocusGained), @@ -305,7 +307,7 @@ where }); tokio::spawn(async move { - loop { + 'main_loop: loop { tokio::select! { Some(Ok(response)) = widget_event_stream.next() => { for widget_event in response.widget_events { @@ -332,6 +334,10 @@ where } Some(Ok(response)) = popup_event_stream.next() => { for popup_event in response.popup_events { + if matches!(popup_event.event, Some(popup_event::Event::Closing(_))) { + break 'main_loop; + }; + let Some(event) = popup_event.event .and_then(|e| e.try_into().ok()) else { continue; From 956533e9d46c108cf7644650e8a0a620b990585f Mon Sep 17 00:00:00 2001 From: phantomas Date: Mon, 30 Mar 2026 01:43:19 +0200 Subject: [PATCH 3/3] snowcap-api: lua: Properly handle close events for surfaces --- api/lua/build/grpc_client.lua | 13 ++++- api/lua/build/src/main.rs | 2 +- api/lua/pinnacle/grpc/defs.lua | 19 +++++-- snowcap/api/lua/snowcap/decoration.lua | 16 ++++++ snowcap/api/lua/snowcap/grpc/defs.lua | 70 ++++++++++++++++++++++---- snowcap/api/lua/snowcap/layer.lua | 8 +++ snowcap/api/lua/snowcap/popup.lua | 8 +++ 7 files changed, 119 insertions(+), 17 deletions(-) diff --git a/api/lua/build/grpc_client.lua b/api/lua/build/grpc_client.lua index fb64d2e94..f50fe4fa8 100644 --- a/api/lua/build/grpc_client.lua +++ b/api/lua/build/grpc_client.lua @@ -314,7 +314,7 @@ end --- ---@param request_specifier grpc_client.RequestSpecifier ---@param data table The message to send. This should be in the structure of `request_specifier.request`. ----@param callback fun(response: table) A callback that will be run with every response +---@param callback fun(response: table): boolean? A callback that will be run with every response ---@param done? fun() A callback that will be run when the stream closes. --- ---@return string|nil error An error string, if any. @@ -355,6 +355,7 @@ function Client:server_streaming_request(request_specifier, data, callback, done self.loop:wrap(function() for response_body in stream:each_chunk() do + local stop = nil while response_body:len() > 0 do local msg_len = string.unpack(">I4", response_body:sub(2, 5)) @@ -369,10 +370,18 @@ function Client:server_streaming_request(request_specifier, data, callback, done end local response = obj - callback(response) + local should_stop = callback(response) + + if should_stop then + stop = true + end response_body = response_body:sub(msg_len + 6) end + + if stop == true then + break + end end if done then diff --git a/api/lua/build/src/main.rs b/api/lua/build/src/main.rs index 0198989ad..32ff96f3d 100644 --- a/api/lua/build/src/main.rs +++ b/api/lua/build/src/main.rs @@ -314,7 +314,7 @@ end"#, ---@nodiscard --- ---@param data {data_ty} ----@param callback fun(response: {ret_ty}) +---@param callback fun(response: {ret_ty}): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any diff --git a/api/lua/pinnacle/grpc/defs.lua b/api/lua/pinnacle/grpc/defs.lua index 774b8da80..2742ad462 100644 --- a/api/lua/pinnacle/grpc/defs.lua +++ b/api/lua/pinnacle/grpc/defs.lua @@ -316,7 +316,7 @@ end --- ---@param request_specifier grpc_client.RequestSpecifier ---@param data table The message to send. This should be in the structure of `request_specifier.request`. ----@param callback fun(response: table) A callback that will be run with every response +---@param callback fun(response: table): boolean? A callback that will be run with every response ---@param done? fun() A callback that will be run when the stream closes. --- ---@return string|nil error An error string, if any. @@ -357,6 +357,7 @@ function Client:server_streaming_request(request_specifier, data, callback, done self.loop:wrap(function() for response_body in stream:each_chunk() do + local stop = nil while response_body:len() > 0 do local msg_len = string.unpack(">I4", response_body:sub(2, 5)) @@ -371,10 +372,18 @@ function Client:server_streaming_request(request_specifier, data, callback, done end local response = obj - callback(response) + local should_stop = callback(response) + + if should_stop then + stop = true + end response_body = response_body:sub(msg_len + 6) end + + if stop == true then + break + end end if done then @@ -1835,7 +1844,7 @@ pinnacle.input.v1.InputService.KeybindStream.response = ".pinnacle.input.v1.Keyb ---@nodiscard --- ---@param data pinnacle.input.v1.KeybindStreamRequest ----@param callback fun(response: pinnacle.input.v1.KeybindStreamResponse) +---@param callback fun(response: pinnacle.input.v1.KeybindStreamResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1855,7 +1864,7 @@ pinnacle.input.v1.InputService.MousebindStream.response = ".pinnacle.input.v1.Mo ---@nodiscard --- ---@param data pinnacle.input.v1.MousebindStreamRequest ----@param callback fun(response: pinnacle.input.v1.MousebindStreamResponse) +---@param callback fun(response: pinnacle.input.v1.MousebindStreamResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -2512,7 +2521,7 @@ pinnacle.process.v1.ProcessService.WaitOnSpawn.response = ".pinnacle.process.v1. ---@nodiscard --- ---@param data pinnacle.process.v1.WaitOnSpawnRequest ----@param callback fun(response: pinnacle.process.v1.WaitOnSpawnResponse) +---@param callback fun(response: pinnacle.process.v1.WaitOnSpawnResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any diff --git a/snowcap/api/lua/snowcap/decoration.lua b/snowcap/api/lua/snowcap/decoration.lua index 99c1137b1..4778468b9 100644 --- a/snowcap/api/lua/snowcap/decoration.lua +++ b/snowcap/api/lua/snowcap/decoration.lua @@ -119,6 +119,22 @@ function decoration.new_widget(args) created = widget.SurfaceHandle.from_decoration_handle(handle), }) + err = client:snowcap_decoration_v1_DecorationService_GetDecorationEvents({ + decoration_id = decoration_id, + }, function(response) ---@diagnostic disable-line:redefined-local + response.decoration_events = response.decoration_events or {} + + for _, decoration_event in ipairs(response.decoration_events) do + if decoration_event.closing ~= nil then + return true + end + end + end, function() + args.program:event({ + closing = {}, + }) + end) + err = client:snowcap_widget_v1_WidgetService_GetWidgetEvents({ decoration_id = decoration_id, }, function(response) ---@diagnostic disable-line: redefined-local diff --git a/snowcap/api/lua/snowcap/grpc/defs.lua b/snowcap/api/lua/snowcap/grpc/defs.lua index c3a1d4c89..a286e1670 100644 --- a/snowcap/api/lua/snowcap/grpc/defs.lua +++ b/snowcap/api/lua/snowcap/grpc/defs.lua @@ -316,7 +316,7 @@ end --- ---@param request_specifier grpc_client.RequestSpecifier ---@param data table The message to send. This should be in the structure of `request_specifier.request`. ----@param callback fun(response: table) A callback that will be run with every response +---@param callback fun(response: table): boolean? A callback that will be run with every response ---@param done? fun() A callback that will be run when the stream closes. --- ---@return string|nil error An error string, if any. @@ -357,6 +357,7 @@ function Client:server_streaming_request(request_specifier, data, callback, done self.loop:wrap(function() for response_body in stream:each_chunk() do + local stop = nil while response_body:len() > 0 do local msg_len = string.unpack(">I4", response_body:sub(2, 5)) @@ -371,10 +372,18 @@ function Client:server_streaming_request(request_specifier, data, callback, done end local response = obj - callback(response) + local should_stop = callback(response) + + if should_stop then + stop = true + end response_body = response_body:sub(msg_len + 6) end + + if stop == true then + break + end end if done then @@ -1138,6 +1147,17 @@ local snowcap_popup_v1_PopupEvent_Focus = { ---@class snowcap.decoration.v1.ViewResponse +---@class snowcap.decoration.v1.GetDecorationEventsRequest +---@field decoration_id integer? + +---@class snowcap.decoration.v1.DecorationEvent +---@field closing snowcap.decoration.v1.DecorationEvent.Closing? + +---@class snowcap.decoration.v1.DecorationEvent.Closing + +---@class snowcap.decoration.v1.GetDecorationEventsResponse +---@field decoration_events snowcap.decoration.v1.DecorationEvent[]? + ---@class snowcap.input.v0alpha1.Modifiers ---@field shift boolean? ---@field ctrl boolean? @@ -1337,6 +1357,9 @@ local snowcap_popup_v1_PopupEvent_Focus = { ---@class snowcap.layer.v1.LayerEvent ---@field focus snowcap.layer.v1.LayerEvent.Focus? +---@field closing snowcap.layer.v1.LayerEvent.Closing? + +---@class snowcap.layer.v1.LayerEvent.Closing ---@class snowcap.layer.v1.GetLayerEventsResponse ---@field layer_events snowcap.layer.v1.LayerEvent[]? @@ -1411,6 +1434,9 @@ local snowcap_popup_v1_PopupEvent_Focus = { ---@class snowcap.popup.v1.PopupEvent ---@field focus snowcap.popup.v1.PopupEvent.Focus? +---@field closing snowcap.popup.v1.PopupEvent.Closing? + +---@class snowcap.popup.v1.PopupEvent.Closing ---@class snowcap.popup.v1.GetPopupEventsResponse ---@field popup_events snowcap.popup.v1.PopupEvent[]? @@ -1498,6 +1524,10 @@ snowcap.decoration.v1.UpdateDecorationRequest = {} snowcap.decoration.v1.UpdateDecorationResponse = {} snowcap.decoration.v1.ViewRequest = {} snowcap.decoration.v1.ViewResponse = {} +snowcap.decoration.v1.GetDecorationEventsRequest = {} +snowcap.decoration.v1.DecorationEvent = {} +snowcap.decoration.v1.DecorationEvent.Closing = {} +snowcap.decoration.v1.GetDecorationEventsResponse = {} snowcap.input = {} snowcap.input.v0alpha1 = {} snowcap.input.v0alpha1.Modifiers = {} @@ -1542,6 +1572,7 @@ snowcap.layer.v1.ViewRequest = {} snowcap.layer.v1.ViewResponse = {} snowcap.layer.v1.GetLayerEventsRequest = {} snowcap.layer.v1.LayerEvent = {} +snowcap.layer.v1.LayerEvent.Closing = {} snowcap.layer.v1.GetLayerEventsResponse = {} snowcap.popup = {} snowcap.popup.v1 = {} @@ -1560,6 +1591,7 @@ snowcap.popup.v1.ViewRequest = {} snowcap.popup.v1.ViewResponse = {} snowcap.popup.v1.GetPopupEventsRequest = {} snowcap.popup.v1.PopupEvent = {} +snowcap.popup.v1.PopupEvent.Closing = {} snowcap.popup.v1.GetPopupEventsResponse = {} snowcap.v0alpha1 = {} snowcap.v0alpha1.Nothing = {} @@ -1602,7 +1634,7 @@ snowcap.widget.v1.WidgetService.GetWidgetEvents.response = ".snowcap.widget.v1.G ---@nodiscard --- ---@param data snowcap.widget.v1.GetWidgetEventsRequest ----@param callback fun(response: snowcap.widget.v1.GetWidgetEventsResponse) +---@param callback fun(response: snowcap.widget.v1.GetWidgetEventsResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1695,6 +1727,26 @@ snowcap.decoration.v1.DecorationService.RequestView.response = ".snowcap.decorat function Client:snowcap_decoration_v1_DecorationService_RequestView(data) return self:unary_request(snowcap.decoration.v1.DecorationService.RequestView, data) end +snowcap.decoration.v1.DecorationService.GetDecorationEvents = {} +snowcap.decoration.v1.DecorationService.GetDecorationEvents.service = "snowcap.decoration.v1.DecorationService" +snowcap.decoration.v1.DecorationService.GetDecorationEvents.method = "GetDecorationEvents" +snowcap.decoration.v1.DecorationService.GetDecorationEvents.request = ".snowcap.decoration.v1.GetDecorationEventsRequest" +snowcap.decoration.v1.DecorationService.GetDecorationEvents.response = ".snowcap.decoration.v1.GetDecorationEventsResponse" + +---Performs a server-streaming request. +--- +---`callback` will be called with every streamed response. +--- +---@nodiscard +--- +---@param data snowcap.decoration.v1.GetDecorationEventsRequest +---@param callback fun(response: snowcap.decoration.v1.GetDecorationEventsResponse): boolean? +---@param done? fun() +--- +---@return string | nil An error string, if any +function Client:snowcap_decoration_v1_DecorationService_GetDecorationEvents(data, callback, done) + return self:server_streaming_request(snowcap.decoration.v1.DecorationService.GetDecorationEvents, data, callback, done) +end snowcap.input.v0alpha1.InputService = {} snowcap.input.v0alpha1.InputService.KeyboardKey = {} snowcap.input.v0alpha1.InputService.KeyboardKey.service = "snowcap.input.v0alpha1.InputService" @@ -1709,7 +1761,7 @@ snowcap.input.v0alpha1.InputService.KeyboardKey.response = ".snowcap.input.v0alp ---@nodiscard --- ---@param data snowcap.input.v0alpha1.KeyboardKeyRequest ----@param callback fun(response: snowcap.input.v0alpha1.KeyboardKeyResponse) +---@param callback fun(response: snowcap.input.v0alpha1.KeyboardKeyResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1729,7 +1781,7 @@ snowcap.input.v0alpha1.InputService.PointerButton.response = ".snowcap.input.v0a ---@nodiscard --- ---@param data snowcap.input.v0alpha1.PointerButtonRequest ----@param callback fun(response: snowcap.input.v0alpha1.PointerButtonResponse) +---@param callback fun(response: snowcap.input.v0alpha1.PointerButtonResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1750,7 +1802,7 @@ snowcap.input.v1.InputService.KeyboardKey.response = ".snowcap.input.v1.Keyboard ---@nodiscard --- ---@param data snowcap.input.v1.KeyboardKeyRequest ----@param callback fun(response: snowcap.input.v1.KeyboardKeyResponse) +---@param callback fun(response: snowcap.input.v1.KeyboardKeyResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1770,7 +1822,7 @@ snowcap.input.v1.InputService.PointerButton.response = ".snowcap.input.v1.Pointe ---@nodiscard --- ---@param data snowcap.input.v1.PointerButtonRequest ----@param callback fun(response: snowcap.input.v1.PointerButtonResponse) +---@param callback fun(response: snowcap.input.v1.PointerButtonResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -1911,7 +1963,7 @@ snowcap.layer.v1.LayerService.GetLayerEvents.response = ".snowcap.layer.v1.GetLa ---@nodiscard --- ---@param data snowcap.layer.v1.GetLayerEventsRequest ----@param callback fun(response: snowcap.layer.v1.GetLayerEventsResponse) +---@param callback fun(response: snowcap.layer.v1.GetLayerEventsResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any @@ -2017,7 +2069,7 @@ snowcap.popup.v1.PopupService.GetPopupEvents.response = ".snowcap.popup.v1.GetPo ---@nodiscard --- ---@param data snowcap.popup.v1.GetPopupEventsRequest ----@param callback fun(response: snowcap.popup.v1.GetPopupEventsResponse) +---@param callback fun(response: snowcap.popup.v1.GetPopupEventsResponse): boolean? ---@param done? fun() --- ---@return string | nil An error string, if any diff --git a/snowcap/api/lua/snowcap/layer.lua b/snowcap/api/lua/snowcap/layer.lua index ef1842de9..833a2db93 100644 --- a/snowcap/api/lua/snowcap/layer.lua +++ b/snowcap/api/lua/snowcap/layer.lua @@ -177,6 +177,10 @@ function layer.new_widget(args) response.layer_events = response.layer_events or {} for _, layer_event in ipairs(response.layer_events) do + if layer_event.closing ~= nil then + return true + end + local focus = layer_event.focus --[[@as snowcap.layer.FocusEvent]] ---@type snowcap.widget.SurfaceEvent? local event = nil @@ -204,6 +208,10 @@ function layer.new_widget(args) if err then log.error(err) end + end, function() + args.program:event({ + closing = {}, + }) end) err = client:snowcap_widget_v1_WidgetService_GetWidgetEvents({ diff --git a/snowcap/api/lua/snowcap/popup.lua b/snowcap/api/lua/snowcap/popup.lua index 19e02415a..1b7e191a3 100644 --- a/snowcap/api/lua/snowcap/popup.lua +++ b/snowcap/api/lua/snowcap/popup.lua @@ -310,6 +310,10 @@ function popup.new_widget(args) response.popup_events = response.popup_events or {} for _, popup_event in ipairs(response.popup_events) do + if popup_event.closing ~= nil then + return true + end + local focus = popup_event.focus --[[@as snowcap.popup.FocusEvent]] ---@type snowcap.widget.SurfaceEvent? local event = nil @@ -337,6 +341,10 @@ function popup.new_widget(args) if err then log.error(err) end + end, function() + args.program:event({ + closing = {}, + }) end) err = client:snowcap_widget_v1_WidgetService_GetWidgetEvents({