bar dropdown backend logic

This commit is contained in:
galister
2026-01-05 20:36:44 +09:00
parent b56aa1a8de
commit ac9bfc9fc4
18 changed files with 476 additions and 185 deletions

View File

@@ -1 +1,2 @@
style_edition = "2024" style_edition = "2024"
edition = "2024"

View File

@@ -3,7 +3,7 @@ use std::ffi::OsStr;
use std::io::Read; use std::io::Read;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum AssetPath<'a> { pub enum AssetPath<'a> {
WguiInternal(&'a str), // tied to internal wgui AssetProvider. Used internally WguiInternal(&'a str), // tied to internal wgui AssetProvider. Used internally
BuiltIn(&'a str), // tied to user AssetProvider BuiltIn(&'a str), // tied to user AssetProvider
@@ -12,7 +12,7 @@ pub enum AssetPath<'a> {
} }
// see AssetPath above for documentation // see AssetPath above for documentation
#[derive(Clone)] #[derive(Debug, Clone)]
pub enum AssetPathOwned { pub enum AssetPathOwned {
WguiInternal(PathBuf), WguiInternal(PathBuf),
BuiltIn(PathBuf), BuiltIn(PathBuf),

View File

@@ -1,27 +1,17 @@
use std::fmt::Debug; use std::fmt::Debug;
pub trait LogErr { pub trait LogErr {
fn log_err(self) -> Self; fn log_err(self, additional: &str) -> Self;
fn log_err_with(self, additional: &str) -> Self; fn log_err_with<D: Debug>(self, additional: &D) -> Self;
fn log_warn(self) -> Self; fn log_warn(self, additional: &str) -> Self;
fn log_warn_with(self, additional: &str) -> Self; fn log_warn_with<D: Debug>(self, additional: &D) -> Self;
} }
impl<T, E> LogErr for Result<T, E> impl<T, E> LogErr for Result<T, E>
where where
E: Debug + Send + Sync + 'static, E: Debug + Send + Sync + 'static,
{ {
fn log_warn(self) -> Result<T, E> { fn log_warn(self, additional: &str) -> Result<T, E> {
match self {
Ok(ok) => Ok(ok),
Err(error) => {
log::warn!("{error:?}");
Err(error)
}
}
}
fn log_warn_with(self, additional: &str) -> Result<T, E> {
match self { match self {
Ok(ok) => Ok(ok), Ok(ok) => Ok(ok),
Err(error) => { Err(error) => {
@@ -31,17 +21,17 @@ where
} }
} }
fn log_err(self) -> Result<T, E> { fn log_warn_with<D: Debug>(self, additional: &D) -> Result<T, E> {
match self { match self {
Ok(ok) => Ok(ok), Ok(ok) => Ok(ok),
Err(error) => { Err(error) => {
log::error!("{error:?}"); log::warn!("{additional:?}: {error:?}");
Err(error) Err(error)
} }
} }
} }
fn log_err_with(self, additional: &str) -> Result<T, E> { fn log_err(self, additional: &str) -> Result<T, E> {
match self { match self {
Ok(ok) => Ok(ok), Ok(ok) => Ok(ok),
Err(error) => { Err(error) => {
@@ -50,4 +40,14 @@ where
} }
} }
} }
fn log_err_with<D: Debug>(self, additional: &D) -> Self {
match self {
Ok(ok) => Ok(ok),
Err(error) => {
log::error!("{additional:?}: {error:?}");
Err(error)
}
}
}
} }

View File

@@ -10,16 +10,11 @@ mod widget_rectangle;
mod widget_sprite; mod widget_sprite;
use crate::{ use crate::{
assets::{normalize_path, AssetPath, AssetPathOwned}, assets::{normalize_path, AssetPath, AssetPathOwned}, components::{Component, ComponentWeak}, drawing::{self}, globals::WguiGlobals, layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair}, log::LogErr, parser::{
components::{Component, ComponentWeak},
drawing::{self},
globals::WguiGlobals,
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
parser::{
component_button::parse_component_button, component_checkbox::{parse_component_checkbox, CheckboxKind}, component_radio_group::parse_component_radio_group, component_slider::parse_component_slider, widget_div::parse_widget_div, widget_image::parse_widget_image, widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite component_button::parse_component_button, component_checkbox::{parse_component_checkbox, CheckboxKind}, component_radio_group::parse_component_radio_group, component_slider::parse_component_slider, widget_div::parse_widget_div, widget_image::parse_widget_image, widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite
}, }, widget::ConstructEssentials
widget::ConstructEssentials,
}; };
use anyhow::Context;
use ouroboros::self_referencing; use ouroboros::self_referencing;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{cell::RefMut, collections::HashMap, path::Path, rc::Rc}; use std::{cell::RefMut, collections::HashMap, path::Path, rc::Rc};
@@ -1109,7 +1104,7 @@ fn get_doc_from_asset_path(
allow_dtd: true, allow_dtd: true,
..Default::default() ..Default::default()
}; };
roxmltree::Document::parse_with_options(xml, opt).unwrap() roxmltree::Document::parse_with_options(xml, opt).context("Unable to parse XML").log_err_with(&asset_path).unwrap()
})); }));
let root = document.borrow_doc().root(); let root = document.borrow_doc().root();

View File

@@ -13,11 +13,6 @@
width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}" width="${width}" height="${height}" min_width="${width}" min_height="${height}" max_width="${width}" max_height="${height}"
/> />
<macro name="button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="8"
align_items="center" justify_content="center" padding="8" width="80" height="80" />
<macro name="bg_rect" width="100%" color="~color_bg" round="16" border="2" border_color="~color_accent" />
<template name="VerticalSeparator"> <template name="VerticalSeparator">
<rectangle width="2" height="100%" color="~color_accent" /> <rectangle width="2" height="100%" color="~color_accent" />
</template> </template>
@@ -85,37 +80,91 @@
</div> </div>
</template> </template>
<macro name="button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="8"
align_items="center" justify_content="center" padding="8" width="80" height="80" overflow="visible"/>
<macro name="menu_button_style" border="2" border_color="~color_accent_translucent" color="~color_bg" round="8"
align_items="center" justify_content="center" padding="8" width="100%" height="60" />
<macro name="bg_rect" width="100%" color="~color_bg" round="16" border="2" border_color="~color_accent" />
<macro name="dropdown_root" new_pass="1" width="200" color="~color_bg" flex_direction="column" position="absolute" margin_top="80" display="none" />
<macro name="dropdown_title" color="~color_faded_50" width="100%" padding="16" overflow="hidden" />
<template name="MenuButton">
<Button macro="menu_button_style" _release="${action}">
<label translation="${translation}" size="24" />
</Button>
</template>
<!-- An app with a single icon. --> <!-- An app with a single icon. -->
<template name="App"> <template name="App">
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.APP;${name}" tooltip_side="bottom"> <div>
<sprite width="56" height="56" color="~text_color" src_ext="${icon}" /> <Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
</Button> <sprite width="56" height="56" color="~text_color" src_ext="${icon}" />
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_HIDE" action="::OverlaySoftToggle ${name}" />
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
<MenuButton translation="BAR.CLOSE" action="::WvrOverlayTermProcess ${name}" />
<MenuButton translation="BAR.FORCE_CLOSE" action="::WvrOverlayKillProcess ${name}" />
</div>
</div>
</template> </template>
<!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 --> <!-- A screen with a shortened connector name, e.g. "H1" for HDMI-A-1 or "D2" for DP-2 -->
<template name="Screen"> <template name="Screen">
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.SCREEN;${name}" tooltip_side="bottom"> <div>
<sprite width="56" height="56" color="~text_color" src_builtin="edit/screen.svg" /> <Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<div position="absolute" margin_top="-10"> <sprite width="56" height="56" color="~text_color" src_builtin="edit/screen.svg" />
<label text="${display}" size="26" color="~color_faded_20" weight="bold" /> <div position="absolute" margin_top="-10">
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
</div>
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_HIDE" action="::OverlaySoftToggle ${name}" />
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
</div> </div>
</Button> </div>
</template> </template>
<template name="Panel"> <template name="Panel">
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.PANEL;${name}" tooltip_side="bottom"> <div>
<sprite width="56" height="56" color="~text_color" src_builtin="edit/panel.svg" /> <Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
</Button> <sprite width="56" height="56" color="~text_color" src_builtin="edit/panel.svg" />
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_HIDE" action="::OverlaySoftToggle ${name}" />
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
</div>
</div>
</template> </template>
<template name="Mirror"> <template name="Mirror">
<Button macro="button_style" id="overlay_${idx}" _short_release="::OverlayToggle ${name}" tooltip="OVERLAY_TOOLTIP.MIRROR;${name}" tooltip_side="bottom"> <div>
<sprite width="56" height="56" color="~text_color" src_builtin="edit/mirror.svg" /> <Button macro="button_style" id="overlay_${idx}" _press="::OverlayToggle ${name}" _release="::ElementSetDisplay dropdown_${idx} none">
<div position="absolute" margin_top="7" margin_left="20"> <sprite width="56" height="56" color="~text_color" src_builtin="edit/mirror.svg" />
<label text="${display}" size="26" color="~color_faded_20" weight="bold" /> <div position="absolute" margin_top="7" margin_left="20">
<label text="${display}" size="26" color="~color_faded_20" weight="bold" />
</div>
</Button>
<div macro="dropdown_root" id="dropdown_${idx}">
<rectangle macro="dropdown_title">
<label text="${name}" weight="bold" size="24" />
</rectangle>
<MenuButton translation="BAR.TOGGLE_HIDE" action="::OverlaySoftToggle ${name}" />
<MenuButton translation="BAR.TOGGLE_IN_SET" action="::OverlayToggle ${name}" />
<MenuButton translation="BAR.CLOSE" action="::OverlayDrop ${name}" />
</div> </div>
</Button> </div>
</template> </template>
<template name="Set"> <template name="Set">

View File

@@ -2,6 +2,12 @@
"ANCHOR": { "ANCHOR": {
"CENTER": "Center" "CENTER": "Center"
}, },
"BAR": {
"HIDE": "Hide",
"TOGGLE_IN_SET": "Toggle in set",
"CLOSE": "Close",
"FORCE_CLOSE": "Force close"
},
"DEFAULT": "Default", "DEFAULT": "Default",
"DISABLED": "Disabled", "DISABLED": "Disabled",
"OVERLAY_TOOLTIP": { "OVERLAY_TOOLTIP": {

View File

@@ -8,7 +8,7 @@ use std::{
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
backend::input, backend::{input, wayvr::process::KillSignal},
state::AppState, state::AppState,
windowing::{OverlaySelector, window::OverlayWindowConfig}, windowing::{OverlaySelector, window::OverlayWindowConfig},
}; };

View File

@@ -11,15 +11,18 @@ use process::ProcessVec;
use slotmap::SecondaryMap; use slotmap::SecondaryMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use smithay::{ use smithay::{
desktop::PopupManager, input::{keyboard::XkbConfig, SeatState}, output::{Mode, Output}, reexports::wayland_server::{self, backend::ClientId}, wayland::{ desktop::PopupManager,
compositor::{self, with_states, SurfaceData}, input::{SeatState, keyboard::XkbConfig},
output::{Mode, Output},
reexports::wayland_server::{self, backend::ClientId},
wayland::{
compositor::{self, SurfaceData, with_states},
dmabuf::{DmabufFeedbackBuilder, DmabufState}, dmabuf::{DmabufFeedbackBuilder, DmabufState},
selection::data_device::DataDeviceState, selection::data_device::DataDeviceState,
shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData}, shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData},
shm::ShmState, shm::ShmState,
} },
}; };
use wlx_common::desktop_finder::DesktopFinder;
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@@ -32,18 +35,23 @@ use vulkano::image::view::ImageView;
use wayvr_ipc::packet_server; use wayvr_ipc::packet_server;
use wgui::gfx::WGfx; use wgui::gfx::WGfx;
use wlx_capture::frame::Transform; use wlx_capture::frame::Transform;
use wlx_common::desktop_finder::DesktopFinder;
use xkbcommon::xkb; use xkbcommon::xkb;
use crate::{ use crate::{
backend::{ backend::{
task::{OverlayTask, TaskType}, task::{OverlayTask, TaskType},
wayvr::{image_importer::ImageImporter, process::{Process}, window::Window}, wayvr::{
image_importer::ImageImporter,
process::{KillSignal, Process},
window::Window,
},
}, },
graphics::WGfxExtras, graphics::WGfxExtras,
ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal}, ipc::{event_queue::SyncEventQueue, ipc_server, signal::WayVRSignal},
overlays::wayvr::create_wl_window_overlay, overlays::wayvr::create_wl_window_overlay,
state::AppState, state::AppState,
subsystem::hid::{WheelDelta, MODS_TO_KEYS}, subsystem::hid::{MODS_TO_KEYS, WheelDelta},
windowing::{OverlayID, OverlaySelector}, windowing::{OverlayID, OverlaySelector},
}; };
@@ -79,7 +87,7 @@ pub enum WayVRTask {
NewToplevel(ClientId, ToplevelSurface), NewToplevel(ClientId, ToplevelSurface),
DropToplevel(ClientId, ToplevelSurface), DropToplevel(ClientId, ToplevelSurface),
NewExternalProcess(ExternalProcessRequest), NewExternalProcess(ExternalProcessRequest),
ProcessTerminationRequest(process::ProcessHandle), ProcessTerminationRequest(process::ProcessHandle, KillSignal),
CloseWindowRequest(window::WindowHandle), CloseWindowRequest(window::WindowHandle),
} }
@@ -303,31 +311,43 @@ impl WvrServerState {
}; };
// Size, icon & fallback title comes from process // Size, icon & fallback title comes from process
let ([size_x, size_y], fallback_title, icon, is_cage) = match wvr_server.processes.get(&process_handle) { let ([size_x, size_y], fallback_title, icon, is_cage) =
Some(Process::Managed(p)) => (p.resolution, Some(p.app_name.clone()), p.icon.as_ref().cloned(), p.exec_path.ends_with("cage")), match wvr_server.processes.get(&process_handle) {
_ => ([1920, 1080], None, None, false) Some(Process::Managed(p)) => (
}; p.resolution,
Some(p.app_name.clone()),
p.icon.as_ref().cloned(),
p.exec_path.ends_with("cage"),
),
_ => ([1920, 1080], None, None, false),
};
let window_handle = wvr_server let window_handle = wvr_server.wm.create_window(
.wm toplevel.clone(),
.create_window(toplevel.clone(), process_handle, size_x, size_y); process_handle,
size_x,
size_y,
);
let mut title: Arc<str> = fallback_title.unwrap_or_else(|| format!("P{}", client.pid)).into(); let mut title: Arc<str> = fallback_title
.unwrap_or_else(|| format!("P{}", client.pid))
.into();
let mut icon = icon; let mut icon = icon;
// Try to get title from xdg_toplevel, unless it's running in cage // Try to get title from xdg_toplevel, unless it's running in cage
if !is_cage { if !is_cage {
let mut needs_title = true; let mut needs_title = true;
let (xdg_title, app_id): (Option<String>, Option<String>) = with_states(toplevel.wl_surface(), |states| { let (xdg_title, app_id): (Option<String>, Option<String>) =
states with_states(toplevel.wl_surface(), |states| {
.data_map states
.get::<XdgToplevelSurfaceData>() .data_map
.map(|t| { .get::<XdgToplevelSurfaceData>()
let t = t.lock().unwrap(); .map(|t| {
(t.title.clone(), t.app_id.clone()) let t = t.lock().unwrap();
}) (t.title.clone(), t.app_id.clone())
.unwrap_or((None, None)) })
}); .unwrap_or((None, None))
});
if let Some(xdg_title) = xdg_title { if let Some(xdg_title) = xdg_title {
needs_title = false; needs_title = false;
title = xdg_title.into(); title = xdg_title.into();
@@ -335,15 +355,17 @@ impl WvrServerState {
// Try to get title & icon from desktop entry // Try to get title & icon from desktop entry
if let Some(app_id) = app_id if let Some(app_id) = app_id
&& let Some(desktop_entry) = app.desktop_finder.get_cached_entry(&app_id) { && let Some(desktop_entry) =
app.desktop_finder.get_cached_entry(&app_id)
{
if needs_title { if needs_title {
title = desktop_entry.app_name.as_ref().into(); title = desktop_entry.app_name.as_ref().into();
} }
if icon.is_none() if icon.is_none()
&& let Some(icon_path) = desktop_entry.icon_path.as_ref() { && let Some(icon_path) = desktop_entry.icon_path.as_ref()
{
icon = Some(icon_path.as_ref().into()); icon = Some(icon_path.as_ref().into());
} }
} }
} }
@@ -352,7 +374,7 @@ impl WvrServerState {
Some(icon) => icon, Some(icon) => icon,
None => DesktopFinder::create_icon(&*title)?.into(), None => DesktopFinder::create_icon(&*title)?.into(),
}; };
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
OverlaySelector::Nothing, OverlaySelector::Nothing,
Box::new(move |app: &mut AppState| { Box::new(move |app: &mut AppState| {
@@ -362,7 +384,10 @@ impl WvrServerState {
window_handle, window_handle,
icon, icon,
[size_x, size_y], [size_x, size_y],
).context("Could not create WvrWindow overlay").inspect_err(|e| log::warn!("{e:?}")).ok() )
.context("Could not create WvrWindow overlay")
.inspect_err(|e| log::warn!("{e:?}"))
.ok()
}), }),
))); )));
@@ -393,30 +418,38 @@ impl WvrServerState {
wvr_server.wm.remove_window(window_handle); wvr_server.wm.remove_window(window_handle);
} }
} }
WayVRTask::ProcessTerminationRequest(process_handle) => { WayVRTask::ProcessTerminationRequest(process_handle, signal) => {
if let Some(process) = wvr_server.processes.get_mut(&process_handle) { if let Some(process) = wvr_server.processes.get_mut(&process_handle) {
process.terminate(); process.kill(signal);
} }
for (h,w) in wvr_server.wm.windows.iter() { // Don't clean up all windows in case of SIGTERM,
// the app might display a confirmation dialog, etc.
if !matches!(signal, KillSignal::Kill) {
continue;
}
for (h, w) in wvr_server.wm.windows.iter() {
if w.process != process_handle { if w.process != process_handle {
continue; continue;
} }
let Some(oid) = wvr_server.window_to_overlay.get(&h) else { let Some(oid) = wvr_server.window_to_overlay.get(&h) else {
continue; continue;
}; };
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop(
OverlaySelector::Id(*oid), OverlaySelector::Id(*oid),
))); )));
} }
} }
WayVRTask::CloseWindowRequest(window_handle) => { WayVRTask::CloseWindowRequest(window_handle) => {
if let Some(w) = wvr_server.wm.windows.get(&window_handle) { if let Some(w) = wvr_server.wm.windows.get(&window_handle) {
log::info!("Sending window close to {window_handle:?}"); log::info!("Sending window close to {window_handle:?}");
w.toplevel.send_close(); w.toplevel.send_close();
} else { } else {
log::warn!("Could not close window - no such handle found: {window_handle:?}"); log::warn!(
"Could not close window - no such handle found: {window_handle:?}"
);
} }
} }
} }
@@ -434,11 +467,15 @@ impl WvrServerState {
Ok(tasks) Ok(tasks)
} }
pub fn terminate_process(&mut self, process_handle: process::ProcessHandle) { pub fn terminate_process(
&mut self,
process_handle: process::ProcessHandle,
signal: KillSignal,
) {
self.tasks self.tasks
.send(WayVRTask::ProcessTerminationRequest(process_handle)); .send(WayVRTask::ProcessTerminationRequest(process_handle, signal));
} }
pub fn close_window(&mut self, window_handle: window::WindowHandle) { pub fn close_window(&mut self, window_handle: window::WindowHandle) {
self.tasks self.tasks
.send(WayVRTask::CloseWindowRequest(window_handle)); .send(WayVRTask::CloseWindowRequest(window_handle));

View File

@@ -31,6 +31,12 @@ pub enum Process {
External(ExternalProcess), // External process not directly controlled by us External(ExternalProcess), // External process not directly controlled by us
} }
#[derive(Clone, Copy)]
pub enum KillSignal {
Term,
Kill,
}
impl Process { impl Process {
pub fn is_running(&mut self) -> bool { pub fn is_running(&mut self) -> bool {
match self { match self {
@@ -39,20 +45,25 @@ impl Process {
} }
} }
pub fn terminate(&mut self) { pub fn kill(&mut self, signal: KillSignal) {
let signal = match signal {
KillSignal::Term => libc::SIGTERM,
KillSignal::Kill => libc::SIGKILL,
};
match self { match self {
Self::Managed(p) => p.terminate(), Self::Managed(p) => p.kill(signal),
Self::External(p) => p.terminate(), Self::External(p) => p.kill(signal),
} }
} }
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
match self { match self {
Self::Managed(p) => p.get_name() Self::Managed(p) => p
.get_name()
.or_else(|| p.exec_path.split('/').last().map(String::from)) .or_else(|| p.exec_path.split('/').last().map(String::from))
.unwrap_or_else(|| String::from("unknown")), .unwrap_or_else(|| String::from("unknown")),
Self::External(p) => p.get_name() Self::External(p) => p.get_name().unwrap_or_else(|| String::from("unknown")),
.unwrap_or_else(|| String::from("unknown")),
} }
} }
@@ -79,7 +90,7 @@ impl Drop for WayVRProcess {
self.child.id(), self.child.id(),
self.exec_path.as_str() self.exec_path.as_str()
); );
self.terminate(); self.kill(libc::SIGTERM);
} }
} }
@@ -113,10 +124,10 @@ impl WayVRProcess {
} }
} }
fn terminate(&mut self) { fn kill(&mut self, signal: i32) {
unsafe { unsafe {
// Gracefully stop process // Gracefully stop process
libc::kill(self.child.id() as i32, libc::SIGTERM); libc::kill(self.child.id() as i32, signal);
} }
} }
@@ -147,11 +158,11 @@ impl ExternalProcess {
} }
} }
fn terminate(&mut self) { fn kill(&mut self, signal: i32) {
if self.pid != 0 { if self.pid != 0 {
unsafe { unsafe {
// send SIGINT (^C) // send SIGINT (^C)
libc::kill(self.pid as i32, libc::SIGINT); libc::kill(self.pid as i32, signal);
} }
} }
self.pid = 0; self.pid = 0;

View File

@@ -10,20 +10,28 @@ use std::{
use anyhow::Context; use anyhow::Context;
use wgui::{ use wgui::{
components::button::ComponentButton, components::button::ComponentButton,
event::{CallbackData, CallbackMetadata, EventCallback, EventListenerKind, MouseButtonIndex}, event::{
CallbackData, CallbackMetadata, EventCallback, EventListenerKind, MouseButtonIndex,
StyleSetRequest,
},
layout::Layout, layout::Layout,
parser::CustomAttribsInfoOwned, log::LogErr,
parser::{CustomAttribsInfoOwned, Fetchable, ParserState},
taffy,
widget::EventResult, widget::EventResult,
}; };
use wlx_common::overlays::ToastTopic; use wlx_common::overlays::ToastTopic;
use crate::{ use crate::{
RUNNING, RUNNING,
backend::task::{OverlayTask, PlayspaceTask, TaskType}, backend::{
overlays::{dashboard::DASH_NAME, toast::Toast}, task::{OverlayTask, PlayspaceTask, TaskType},
wayvr::process::KillSignal,
},
overlays::{dashboard::DASH_NAME, toast::Toast, wayvr::WvrCommand},
state::AppState, state::AppState,
subsystem::hid::VirtualKey, subsystem::hid::VirtualKey,
windowing::OverlaySelector, windowing::{OverlaySelector, backend::OverlayEventData},
}; };
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@@ -176,6 +184,7 @@ fn short_duration(btn: &ComponentButton, app: &AppState) -> bool {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(super) fn setup_custom_button<S: 'static>( pub(super) fn setup_custom_button<S: 'static>(
layout: &mut Layout, layout: &mut Layout,
parser_state: &ParserState,
attribs: &CustomAttribsInfoOwned, attribs: &CustomAttribsInfoOwned,
_app: &AppState, _app: &AppState,
button: Rc<ComponentButton>, button: Rc<ComponentButton>,
@@ -193,6 +202,37 @@ pub(super) fn setup_custom_button<S: 'static>(
let button = button.clone(); let button = button.clone();
let callback: EventCallback<AppState, S> = match command { let callback: EventCallback<AppState, S> = match command {
"::ElementSetDisplay" => {
let (Some(id), Some(value)) = (args.next(), args.next()) else {
log::warn!(
"{command} has incorrect arguments. Should be: {command} <element_id> <display>"
);
return;
};
let Ok(widget_id) = parser_state.data.get_widget_id(id) else {
log::warn!("{command}: no element exists with ID '{id}'");
return;
};
let display = match value {
"none" => taffy::Display::None,
"flex" => taffy::Display::Flex,
"block" => taffy::Display::Block,
"grid" => taffy::Display::Grid,
_ => {
log::warn!("{command} has invalid display argument: '{value}'");
return;
}
};
Box::new(move |common, _data, _app, _| {
common
.alterables
.set_style(widget_id, StyleSetRequest::Display(display));
Ok(EventResult::Consumed)
})
}
"::DashToggle" => Box::new(move |_common, data, app, _| { "::DashToggle" => Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) { if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass); return Ok(EventResult::Pass);
@@ -284,8 +324,84 @@ pub(super) fn setup_custom_button<S: 'static>(
return Ok(EventResult::Pass); return Ok(EventResult::Pass);
} }
app.tasks.enqueue(TaskType::Overlay(OverlayTask::SoftToggleOverlay( app.tasks
.enqueue(TaskType::Overlay(OverlayTask::SoftToggleOverlay(
OverlaySelector::Name(arg.clone()),
)));
Ok(EventResult::Consumed)
})
}
"::OverlayDrop" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
return;
};
Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Drop(OverlaySelector::Name(
arg.clone(),
))));
Ok(EventResult::Consumed)
})
}
"::WvrOverlayCloseWindow" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
return;
};
Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(arg.clone()), OverlaySelector::Name(arg.clone()),
Box::new(move |app, owc| {
let _ = owc
.backend
.notify(app, OverlayEventData::WvrCommand(WvrCommand::CloseWindow))
.log_warn("Could not close window");
}),
)));
Ok(EventResult::Consumed)
})
}
"::WvrOverlayKillProcess" | "::WvrOverlayTermProcess" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
return;
};
let signal = if command == "::OverlayKillProcess" {
KillSignal::Kill
} else {
KillSignal::Term
};
Box::new(move |_common, data, app, _| {
if !test_button(data) || !test_duration(&button, app) {
return Ok(EventResult::Pass);
}
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(arg.clone()),
Box::new(move |app, owc| {
let _ = owc
.backend
.notify(
app,
OverlayEventData::WvrCommand(WvrCommand::KillProcess(signal)),
)
.log_warn("Could not kill process");
}),
))); )));
Ok(EventResult::Consumed) Ok(EventResult::Consumed)
}) })

View File

@@ -186,7 +186,7 @@ impl<S: 'static> GuiPanel<S> {
.parser_state .parser_state
.fetch_component_from_widget_id_as::<ComponentButton>(elem.widget_id) .fetch_component_from_widget_id_as::<ComponentButton>(elem.widget_id)
{ {
setup_custom_button::<S>(&mut self.layout, elem, app, button); setup_custom_button::<S>(&mut self.layout, &self.parser_state, elem, app, button);
} }
if let Some(on_custom_attrib) = &self.on_custom_attrib { if let Some(on_custom_attrib) = &self.on_custom_attrib {

View File

@@ -300,6 +300,8 @@ impl Connection {
params: &mut TickParams, params: &mut TickParams,
process_handle: packet_server::WvrProcessHandle, process_handle: packet_server::WvrProcessHandle,
) { ) {
use crate::backend::wayvr::process::KillSignal;
let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle); let native_handle = &wayvr::process::ProcessHandle::from_packet(process_handle);
let process = params.wvr_server.processes.get_mut(native_handle); let process = params.wvr_server.processes.get_mut(native_handle);
@@ -307,7 +309,7 @@ impl Connection {
return; return;
}; };
process.terminate(); process.kill(KillSignal::Term);
} }
#[cfg(feature = "wayvr")] #[cfg(feature = "wayvr")]

View File

@@ -30,7 +30,10 @@ use crate::{
backend::{ backend::{
input::{Haptics, HoverResult, PointerHit, PointerMode}, input::{Haptics, HoverResult, PointerHit, PointerMode},
task::{OverlayTask, PlayspaceTask, TaskType}, task::{OverlayTask, PlayspaceTask, TaskType},
wayvr::{process::ProcessHandle, window::WindowHandle}, wayvr::{
process::{KillSignal, ProcessHandle},
window::WindowHandle,
},
}, },
ipc::ipc_server::{gen_args_vec, gen_env_vec}, ipc::ipc_server::{gen_args_vec, gen_env_vec},
state::AppState, state::AppState,
@@ -402,7 +405,7 @@ impl DashInterface<AppState> for DashInterfaceLive {
handle: WvrProcessHandle, handle: WvrProcessHandle,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let wvr_server = app.wvr_server.as_mut().unwrap(); let wvr_server = app.wvr_server.as_mut().unwrap();
wvr_server.terminate_process(ProcessHandle::from_packet(handle)); wvr_server.terminate_process(ProcessHandle::from_packet(handle), KillSignal::Term);
Ok(()) Ok(())
} }

View File

@@ -1,15 +1,28 @@
use std::{collections::{HashMap}, rc::Rc, time::Duration}; use std::{collections::HashMap, rc::Rc, time::Duration};
use crate::{ use crate::{
app_misc, app_misc,
gui::{panel::{GuiPanel, NewGuiPanelParams}, timer::GuiTimer}, gui::{
panel::{GuiPanel, NewGuiPanelParams},
timer::GuiTimer,
},
state::AppState, state::AppState,
subsystem::hid::XkbKeymap, windowing::{backend::OverlayEventData, window::OverlayCategory}, subsystem::hid::XkbKeymap,
windowing::{backend::OverlayEventData, window::OverlayCategory},
}; };
use anyhow::Context; use anyhow::Context;
use glam::{FloatExt, Mat4, Vec2, vec2, vec3}; use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
use wgui::{ use wgui::{
animation::{Animation, AnimationEasing}, assets::AssetPath, components::button::ComponentButton, drawing::{self, Color}, event::{self, CallbackDataCommon, CallbackMetadata, EventAlterables, EventListenerKind}, layout::LayoutUpdateParams, parser::{Fetchable, ParseDocumentParams}, renderer_vk::util, taffy::{self, prelude::length}, widget::{div::WidgetDiv, rectangle::WidgetRectangle, EventResult} animation::{Animation, AnimationEasing},
assets::AssetPath,
components::button::ComponentButton,
drawing::{self, Color},
event::{self, CallbackDataCommon, CallbackMetadata, EventAlterables, EventListenerKind},
layout::LayoutUpdateParams,
parser::{Fetchable, ParseDocumentParams},
renderer_vk::util,
taffy::{self, prelude::length},
widget::{EventResult, div::WidgetDiv, rectangle::WidgetRectangle},
}; };
use super::{ use super::{
@@ -276,7 +289,7 @@ pub(super) fn create_keyboard_panel(
for i in 0..num_sets { for i in 0..num_sets {
let mut params = HashMap::new(); let mut params = HashMap::new();
params.insert("idx".into(), i.to_string().into()); params.insert("idx".into(), i.to_string().into());
params.insert("display".into(), (i+1).to_string().into()); params.insert("display".into(), (i + 1).to_string().into());
panel.parser_state.instantiate_template( panel.parser_state.instantiate_template(
&doc_params, &doc_params,
"Set", "Set",
@@ -284,14 +297,16 @@ pub(super) fn create_keyboard_panel(
sets_root, sets_root,
params, params,
)?; )?;
let set_button = panel.parser_state.fetch_component_as::<ComponentButton>(&format!("set_{i}"))?; let set_button = panel
.parser_state
.fetch_component_as::<ComponentButton>(&format!("set_{i}"))?;
if panel.state.current_set == Some(i) { if panel.state.current_set == Some(i) {
let mut com = CallbackDataCommon { let mut com = CallbackDataCommon {
alterables: &mut alterables, alterables: &mut alterables,
state: &panel.layout.state, state: &panel.layout.state,
}; };
set_button.set_sticky_state(&mut com, true); set_button.set_sticky_state(&mut com, true);
} }
panel.state.set_buttons.push(set_button); panel.state.set_buttons.push(set_button);
} }
panel.process_custom_elems(app); panel.process_custom_elems(app);
@@ -308,21 +323,34 @@ pub(super) fn create_keyboard_panel(
let (template, root) = match meta.category { let (template, root) = match meta.category {
OverlayCategory::Screen => { OverlayCategory::Screen => {
params.insert("display".into(), format!("{}{}", (*meta.name).chars().next().unwrap_or_default(), (*meta.name).chars().last().unwrap_or_default()).into()); params.insert(
"display".into(),
format!(
"{}{}",
(*meta.name).chars().next().unwrap_or_default(),
(*meta.name).chars().last().unwrap_or_default()
)
.into(),
);
("Screen", panels_root) ("Screen", panels_root)
}, }
OverlayCategory::Mirror => { OverlayCategory::Mirror => {
params.insert("display".into(), meta.name.as_ref().into()); params.insert("display".into(), meta.name.as_ref().into());
("Mirror", panels_root) ("Mirror", panels_root)
}, }
OverlayCategory::Panel => { OverlayCategory::Panel => ("Panel", panels_root),
("Panel", panels_root)
},
OverlayCategory::WayVR => { OverlayCategory::WayVR => {
params.insert("icon".into(), meta.icon.as_ref().expect("WayVR overlay without Icon attribute!").as_ref().into()); params.insert(
"icon".into(),
meta.icon
.as_ref()
.expect("WayVR overlay without Icon attribute!")
.as_ref()
.into(),
);
("App", apps_root) ("App", apps_root)
}, }
_ => continue _ => continue,
}; };
params.insert("idx".into(), i.to_string().into()); params.insert("idx".into(), i.to_string().into());
@@ -334,14 +362,16 @@ pub(super) fn create_keyboard_panel(
root, root,
params, params,
)?; )?;
let overlay_buttons = panel.parser_state.fetch_component_as::<ComponentButton>(&format!("overlay_{i}"))?; let overlay_buttons = panel
.parser_state
.fetch_component_as::<ComponentButton>(&format!("overlay_{i}"))?;
if meta.visible { if meta.visible {
let mut com = CallbackDataCommon { let mut com = CallbackDataCommon {
alterables: &mut alterables, alterables: &mut alterables,
state: &panel.layout.state, state: &panel.layout.state,
}; };
overlay_buttons.set_sticky_state(&mut com, true); overlay_buttons.set_sticky_state(&mut com, true);
} }
panel.state.overlay_buttons.insert(meta.id, overlay_buttons); panel.state.overlay_buttons.insert(meta.id, overlay_buttons);
} }
panel.process_custom_elems(app); panel.process_custom_elems(app);
@@ -354,8 +384,7 @@ pub(super) fn create_keyboard_panel(
let mut overlay_buttons = panel.state.overlay_buttons.clone(); let mut overlay_buttons = panel.state.overlay_buttons.clone();
for visible in &*overlays { for visible in &*overlays {
if let Some(btn) = overlay_buttons.remove(*visible) if let Some(btn) = overlay_buttons.remove(*visible) {
{
btn.set_sticky_state(&mut com, true); btn.set_sticky_state(&mut com, true);
} }
} }

View File

@@ -1,24 +1,42 @@
use std::{ use std::{
cell::Cell, collections::HashMap, process::{Child, Command}, rc::Rc, sync::atomic::Ordering cell::Cell,
collections::{HashMap, HashSet},
process::{Child, Command},
rc::Rc,
sync::atomic::Ordering,
}; };
use crate::{ use crate::{
backend::input::{HoverResult, PointerHit}, gui::panel::GuiPanel, overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier}, state::AppState, subsystem::{ KEYMAP_CHANGE,
backend::input::{HoverResult, PointerHit},
gui::panel::GuiPanel,
overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier},
state::AppState,
subsystem::{
dbus::DbusConnector, dbus::DbusConnector,
hid::{ hid::{
get_keymap_wl, get_keymap_x11, KeyModifier, VirtualKey, WheelDelta, XkbKeymap, ALT, CTRL, META, SHIFT, SUPER ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey, WheelDelta, XkbKeymap,
get_keymap_wl, get_keymap_x11,
}, },
}, windowing::{ },
backend::{FrameMeta, OverlayBackend, OverlayEventData, OverlayMeta, RenderResources, ShouldRender}, windowing::{
window::{OverlayCategory, OverlayWindowConfig}, OverlayID, OverlayID,
}, KEYMAP_CHANGE backend::{
FrameMeta, OverlayBackend, OverlayEventData, OverlayMeta, RenderResources, ShouldRender,
},
window::{OverlayCategory, OverlayWindowConfig},
},
}; };
use anyhow::Context; use anyhow::Context;
use glam::{Affine3A, Quat, Vec3, vec3}; use glam::{Affine3A, Quat, Vec3, vec3};
use regex::Regex; use regex::Regex;
use slotmap::{new_key_type, SecondaryMap, SlotMap}; use slotmap::{SecondaryMap, SlotMap, new_key_type};
use wgui::{ use wgui::{
components::button::ComponentButton, drawing, event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex} components::button::ComponentButton,
drawing,
event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
layout::{Layout, WidgetID},
parser::ParserState,
}; };
use wlx_common::overlays::{BackendAttrib, BackendAttribValue}; use wlx_common::overlays::{BackendAttrib, BackendAttribValue};
use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::windowing::{OverlayWindowState, Positioning};
@@ -329,7 +347,7 @@ impl KeyboardState {
overlay_buttons overlay_buttons
}, },
overlay_metas: { overlay_metas: {
let mut overlay_metas= SecondaryMap::new(); let mut overlay_metas = SecondaryMap::new();
std::mem::swap(&mut overlay_metas, &mut self.overlay_metas); std::mem::swap(&mut overlay_metas, &mut self.overlay_metas);
overlay_metas overlay_metas
}, },

View File

@@ -28,7 +28,7 @@ use crate::{
backend::{ backend::{
XrBackend, XrBackend,
input::{self, HoverResult}, input::{self, HoverResult},
wayvr::{self, SurfaceBufWithImage, window::WindowHandle}, wayvr::{self, SurfaceBufWithImage, process::KillSignal, window::WindowHandle},
}, },
graphics::{ExtentExt, Vert2Uv, upload_quad_vertices}, graphics::{ExtentExt, Vert2Uv, upload_quad_vertices},
gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS}, gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
@@ -44,6 +44,11 @@ use crate::{
}, },
}; };
pub enum WvrCommand {
CloseWindow,
KillProcess(KillSignal),
}
const BORDER_SIZE: u32 = 5; const BORDER_SIZE: u32 = 5;
const BAR_SIZE: u32 = 48; const BAR_SIZE: u32 = 48;
@@ -421,10 +426,24 @@ impl OverlayBackend for WvrWindowBackend {
app: &mut state::AppState, app: &mut state::AppState,
event_data: OverlayEventData, event_data: OverlayEventData,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if let OverlayEventData::IdAssigned(oid) = event_data { match event_data {
let wvr_server = app.wvr_server.as_mut().unwrap(); //never None OverlayEventData::IdAssigned(oid) => {
wvr_server.overlay_added(oid, self.window); let wvr_server = app.wvr_server.as_mut().unwrap(); //never None
wvr_server.overlay_added(oid, self.window);
}
OverlayEventData::WvrCommand(WvrCommand::CloseWindow) => {
app.wvr_server.as_mut().unwrap().close_window(self.window);
}
OverlayEventData::WvrCommand(WvrCommand::KillProcess(signal)) => {
let wvr_server = app.wvr_server.as_mut().unwrap();
let Some(p) = wvr_server.wm.windows.get(&self.window) else {
return Ok(());
};
wvr_server.terminate_process(p.process, signal);
}
_ => {}
} }
Ok(()) Ok(())
} }

View File

@@ -17,6 +17,7 @@ use crate::{
task::ModifyPanelCommand, task::ModifyPanelCommand,
}, },
graphics::{ExtentExt, RenderResult}, graphics::{ExtentExt, RenderResult},
overlays::wayvr::WvrCommand,
state::AppState, state::AppState,
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
windowing::{OverlayID, window::OverlayCategory}, windowing::{OverlayID, window::OverlayCategory},
@@ -134,6 +135,7 @@ pub enum OverlayEventData {
element: String, element: String,
command: ModifyPanelCommand, command: ModifyPanelCommand,
}, },
WvrCommand(WvrCommand),
} }
pub trait OverlayBackend: Any { pub trait OverlayBackend: Any {

View File

@@ -1,5 +1,7 @@
use std::{ use std::{
collections::{HashMap, VecDeque}, rc::Rc, sync::atomic::Ordering collections::{HashMap, VecDeque},
rc::Rc,
sync::atomic::Ordering,
}; };
use anyhow::Context; use anyhow::Context;
@@ -7,7 +9,9 @@ use glam::{Affine3A, Vec3, Vec3A};
use slotmap::{HopSlotMap, Key, SecondaryMap}; use slotmap::{HopSlotMap, Key, SecondaryMap};
use wgui::log::LogErr; use wgui::log::LogErr;
use wlx_common::{ use wlx_common::{
astr_containers::{AStrMap, AStrMapExt}, config::SerializedWindowSet, overlays::{BackendAttrib, BackendAttribValue, ToastTopic} astr_containers::{AStrMap, AStrMapExt},
config::SerializedWindowSet,
overlays::{BackendAttrib, BackendAttribValue, ToastTopic},
}; };
use crate::{ use crate::{
@@ -65,7 +69,7 @@ where
restore_set: 0, restore_set: 0,
sets: vec![OverlayWindowSet::default()], sets: vec![OverlayWindowSet::default()],
anchor_local: Affine3A::from_translation(Vec3::NEG_Z), anchor_local: Affine3A::from_translation(Vec3::NEG_Z),
watch_id: OverlayID::null(), // set down below watch_id: OverlayID::null(), // set down below
keyboard_id: OverlayID::null(), // set down below keyboard_id: OverlayID::null(), // set down below
edit_mode: false, edit_mode: false,
dropped_overlays: VecDeque::with_capacity(8), dropped_overlays: VecDeque::with_capacity(8),
@@ -149,11 +153,7 @@ where
OverlayEventData::EditModeChanged(false), OverlayEventData::EditModeChanged(false),
OverlayEventData::DevicesChanged, OverlayEventData::DevicesChanged,
] { ] {
me.mut_by_id(id) me.mut_by_id(id).unwrap().config.backend.notify(app, ev)?;
.unwrap()
.config
.backend
.notify(app, ev)?;
} }
} }
@@ -205,11 +205,13 @@ where
self.sets.push(OverlayWindowSet::default()); self.sets.push(OverlayWindowSet::default());
let len = self.sets.len(); let len = self.sets.len();
for id in [self.watch_id, self.keyboard_id] { for id in [self.watch_id, self.keyboard_id] {
self.mut_by_id(id).and_then(|o| o self.mut_by_id(id).map(|o| {
.config let _ = o
.backend .config
.notify(app, OverlayEventData::NumSetsChanged(len)).log_err().ok()) .backend
.context("Could not notify NumSetsChanged")?; .notify(app, OverlayEventData::NumSetsChanged(len))
.log_err("Could not notify NumSetsChanged");
});
} }
} }
OverlayTask::DeleteActiveSet => { OverlayTask::DeleteActiveSet => {
@@ -241,11 +243,13 @@ where
self.sets.remove(set); self.sets.remove(set);
let len = self.sets.len(); let len = self.sets.len();
for id in [self.watch_id, self.keyboard_id] { for id in [self.watch_id, self.keyboard_id] {
self.mut_by_id(id).and_then(|o| o self.mut_by_id(id).map(|o| {
.config let _ = o
.backend .config
.notify(app, OverlayEventData::NumSetsChanged(len)).log_err().ok()) .backend
.context("Could not notify NumSetsChanged")?; .notify(app, OverlayEventData::NumSetsChanged(len))
.log_err("Could not notify NumSetsChanged");
});
} }
} }
OverlayTask::CleanupMirrors => { OverlayTask::CleanupMirrors => {
@@ -740,12 +744,11 @@ impl<T> OverlayWindowManager<T> {
self.current_set = new_set; self.current_set = new_set;
for id in [self.watch_id, self.keyboard_id] { for id in [self.watch_id, self.keyboard_id] {
let _ = self.mut_by_id(id) let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| {
.context("Missing overlay")
.and_then(|o|
o.config o.config
.backend .backend
.notify(app, OverlayEventData::ActiveSetChanged(new_set))); .notify(app, OverlayEventData::ActiveSetChanged(new_set))
});
let _ = self let _ = self
.visible_overlays_changed(app) .visible_overlays_changed(app)
@@ -774,12 +777,14 @@ impl<T> OverlayWindowManager<T> {
if matches!(data.config.category, OverlayCategory::Internal) { if matches!(data.config.category, OverlayCategory::Internal) {
continue; continue;
} }
let icon = if let Some(BackendAttribValue::Icon(icon)) = data.config.backend.get_attrib(BackendAttrib::Icon) { let icon = if let Some(BackendAttribValue::Icon(icon)) =
data.config.backend.get_attrib(BackendAttrib::Icon)
{
Some(icon) Some(icon)
} else { } else {
None None
}; };
meta.push(OverlayMeta { meta.push(OverlayMeta {
id, id,
name: data.config.name.clone(), name: data.config.name.clone(),
@@ -791,12 +796,11 @@ impl<T> OverlayWindowManager<T> {
let meta: Rc<[OverlayMeta]> = meta.into(); let meta: Rc<[OverlayMeta]> = meta.into();
for id in [self.watch_id, self.keyboard_id] { for id in [self.watch_id, self.keyboard_id] {
let _ = self.mut_by_id(id) let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| {
.context("Missing overlay") o.config
.and_then(|o| o .backend
.config .notify(app, OverlayEventData::OverlaysChanged(meta.clone()))
.backend });
.notify(app, OverlayEventData::OverlaysChanged(meta.clone())));
} }
Ok(()) Ok(())
@@ -816,12 +820,11 @@ impl<T> OverlayWindowManager<T> {
let vis: Rc<[OverlayID]> = vis.into(); let vis: Rc<[OverlayID]> = vis.into();
for id in [self.watch_id, self.keyboard_id] { for id in [self.watch_id, self.keyboard_id] {
let _ = self.mut_by_id(id) let _ = self.mut_by_id(id).context("Missing overlay").and_then(|o| {
.context("Missing overlay") o.config
.and_then(|o| o .backend
.config .notify(app, OverlayEventData::VisibleOverlaysChanged(vis.clone()))
.backend });
.notify(app, OverlayEventData::VisibleOverlaysChanged(vis.clone())));
} }
Ok(()) Ok(())