bar dropdown backend logic
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
style_edition = "2024"
|
style_edition = "2024"
|
||||||
|
edition = "2024"
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
|||||||
Reference in New Issue
Block a user