mirror of
https://github.com/wayvr-org/wayvr.git
synced 2026-10-28 16:02:47 +01:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46074fa497 | ||
|
|
3c33128278 |
No files matched your search
Generated
+117
-1
@@ -689,6 +689,12 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -729,6 +735,17 @@ dependencies = [
|
||||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.8.1"
|
||||
@@ -1401,6 +1418,7 @@ dependencies = [
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shell-words",
|
||||
"smol",
|
||||
"strum",
|
||||
"ureq",
|
||||
@@ -1921,6 +1939,21 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||
|
||||
[[package]]
|
||||
name = "freedesktop-desktop-entry"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42a69ad6111a2b572cb41e0f3542d546386e841013cee7c45d705b61a15a87dd"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"gettext-rs",
|
||||
"log",
|
||||
"memchr",
|
||||
"thiserror 2.0.18",
|
||||
"unicase",
|
||||
"xdg 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.3.0"
|
||||
@@ -2100,6 +2133,26 @@ dependencies = [
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gettext-rs"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df5e3fa8c077b15fdfa8ce130e2bc73d663b8aaa6d51b71421b8659980b1d80"
|
||||
dependencies = [
|
||||
"gettext-sys",
|
||||
"locale_config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gettext-sys"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4caa7e4460b196107cc1bb2629488e8b6dbb7f34f6683ea6753c4ac22f590785"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"temp-dir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.14.2"
|
||||
@@ -2869,6 +2922,19 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "locale_config"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
"regex",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
@@ -2930,6 +2996,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.2.0"
|
||||
@@ -3221,6 +3296,26 @@ dependencies = [
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||
dependencies = [
|
||||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-foundation"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||
dependencies = [
|
||||
"block",
|
||||
"objc",
|
||||
"objc_id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.3.5"
|
||||
@@ -3528,6 +3623,15 @@ dependencies = [
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc_id"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
|
||||
dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.37.3"
|
||||
@@ -5343,6 +5447,12 @@ version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "temp-dir"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
@@ -5693,6 +5803,12 @@ dependencies = [
|
||||
"wlx-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.18"
|
||||
@@ -6739,6 +6855,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"freedesktop-desktop-entry",
|
||||
"glam",
|
||||
"glob",
|
||||
"identicons-svg",
|
||||
@@ -6746,7 +6863,6 @@ dependencies = [
|
||||
"idmap-derive",
|
||||
"log",
|
||||
"rodio",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_json5",
|
||||
|
||||
+1
-1
@@ -32,6 +32,7 @@ rust-embed = "8.9.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
serde_json5 = "0.2.1"
|
||||
shell-words = "1.1.1"
|
||||
slotmap = "1.1.1"
|
||||
smallvec = "1.15.2"
|
||||
smol = "2.0.2"
|
||||
@@ -44,7 +45,6 @@ wayland-protocols-misc = { version = "0.3.12" }
|
||||
winit = "0.30.12"
|
||||
xdg = "3.0.0"
|
||||
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
debug = true
|
||||
|
||||
@@ -19,6 +19,7 @@ log.workspace = true
|
||||
rust-embed.workspace = true
|
||||
serde = { workspace = true, features = ["rc"] }
|
||||
serde_json.workspace = true
|
||||
shell-words.workspace = true
|
||||
smol = { workspace = true }
|
||||
strum.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -61,7 +61,7 @@ impl<T> Tab<T> for TabApps<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_entry_from_app_name<'a>(app_id: &str, entries: &'a [DesktopEntry]) -> Option<&'a DesktopEntry> {
|
||||
fn find_entry_from_app_name<'a>(app_id: &str, entries: &'a [Rc<DesktopEntry>]) -> Option<&'a Rc<DesktopEntry>> {
|
||||
entries.iter().find(|&entry| *entry.app_id == *app_id)
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ impl<T> TabApps<T> {
|
||||
|
||||
let mut stale_entries = Vec::<Rc<str>>::new();
|
||||
|
||||
let mut pinned_desktop_entries = Vec::<(PinnedApp, &DesktopEntry)>::new();
|
||||
let mut pinned_desktop_entries = Vec::<(PinnedApp, Rc<DesktopEntry>)>::new();
|
||||
|
||||
// collect pinned desktop entries
|
||||
{
|
||||
@@ -83,7 +83,7 @@ impl<T> TabApps<T> {
|
||||
continue;
|
||||
};
|
||||
|
||||
pinned_desktop_entries.push((pinned_app.clone(), desktop_entry));
|
||||
pinned_desktop_entries.push((pinned_app.clone(), desktop_entry.clone()));
|
||||
}
|
||||
// cleanup:
|
||||
// remove non-existent app ids from pinned apps
|
||||
@@ -119,7 +119,7 @@ impl<T> TabApps<T> {
|
||||
&mut state.parser_state,
|
||||
&doc_params(frontend.globals.clone()),
|
||||
self.pinned_apps_parent,
|
||||
desktop_entry,
|
||||
desktop_entry.as_ref(),
|
||||
Some(tooltip_string),
|
||||
)?;
|
||||
|
||||
@@ -139,8 +139,8 @@ impl<T> TabApps<T> {
|
||||
|
||||
struct AppList {
|
||||
//data: Vec<ParserData>,
|
||||
all_entries: Vec<DesktopEntry>,
|
||||
entries_to_mount: VecDeque<DesktopEntry>,
|
||||
all_entries: Vec<Rc<DesktopEntry>>,
|
||||
entries_to_mount: VecDeque<Rc<DesktopEntry>>,
|
||||
list_parent: WidgetPair,
|
||||
prev_category_name: String,
|
||||
}
|
||||
@@ -150,7 +150,7 @@ fn on_app_click(
|
||||
frontend_tasks: FrontendTasks,
|
||||
tasks: Tasks<Task>,
|
||||
globals: WguiGlobals,
|
||||
entry: DesktopEntry,
|
||||
entry: Rc<DesktopEntry>,
|
||||
state: Rc<RefCell<State>>,
|
||||
pinned_app: Option<PinnedApp>,
|
||||
) -> ButtonClickCallback {
|
||||
@@ -307,14 +307,17 @@ fn get_category_name_score(name: &str) -> u8 {
|
||||
Scores::Unknown as u8
|
||||
}
|
||||
|
||||
fn get_best_category_name(categories: &[Rc<str>]) -> Option<&Rc<str>> {
|
||||
fn get_best_category_name<S>(categories: &[S]) -> Option<&str>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut best_score: u8 = 0;
|
||||
let mut best_category: Option<&Rc<str>> = None;
|
||||
let mut best_category: Option<&str> = None;
|
||||
|
||||
for cat in categories {
|
||||
let score = get_category_name_score(cat);
|
||||
let score = get_category_name_score(cat.as_ref());
|
||||
if score > best_score {
|
||||
best_category = Some(cat);
|
||||
best_category = Some(cat.as_ref());
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ struct LaunchParams<'a, T> {
|
||||
pub struct View {
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
entry: DesktopEntry,
|
||||
entry: Rc<DesktopEntry>,
|
||||
tasks: Tasks<Task>,
|
||||
frontend_tasks: FrontendTasks,
|
||||
globals: WguiGlobals,
|
||||
@@ -80,7 +80,7 @@ pub struct View {
|
||||
|
||||
pub struct Params<'a> {
|
||||
pub globals: &'a WguiGlobals,
|
||||
pub entry: DesktopEntry,
|
||||
pub entry: Rc<DesktopEntry>,
|
||||
pub layout: &'a mut Layout,
|
||||
pub parent_id: WidgetID,
|
||||
pub config: &'a GeneralConfig,
|
||||
@@ -119,10 +119,8 @@ impl View {
|
||||
{
|
||||
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_exec")?;
|
||||
|
||||
label_exec.set_text_simple(
|
||||
&mut params.globals.get(),
|
||||
Translation::from_raw_text_string(format!("{} {}", params.entry.exec_path, params.entry.exec_args)),
|
||||
);
|
||||
let command = shell_words::join(params.entry.exec_argv.iter().map(|x| x.as_ref()));
|
||||
label_exec.set_text_simple(&mut params.globals.get(), Translation::from_raw_text_string(command));
|
||||
}
|
||||
|
||||
let tasks = Tasks::new();
|
||||
@@ -406,14 +404,19 @@ impl View {
|
||||
env.push("ELECTRON_OZONE_PLATFORM_HINT=wayland".into());
|
||||
}
|
||||
|
||||
let args = match params.compositor_mode {
|
||||
AppCompositorMode::Cage => format!("-- {} {}", params.application.exec_path, params.application.exec_args),
|
||||
AppCompositorMode::Native => params.application.exec_args.to_string(),
|
||||
};
|
||||
|
||||
let exec = match params.compositor_mode {
|
||||
AppCompositorMode::Cage => "cage".to_string(),
|
||||
AppCompositorMode::Native => params.application.exec_path.to_string(),
|
||||
let argv = match params.compositor_mode {
|
||||
AppCompositorMode::Cage => ["cage", "--"]
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(params.application.exec_argv.iter().map(|x| x.as_ref()))
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
AppCompositorMode::Native => params
|
||||
.application
|
||||
.exec_argv
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
|
||||
let pos_mode = match params.pos_mode {
|
||||
@@ -423,7 +426,7 @@ impl View {
|
||||
};
|
||||
|
||||
let mut userdata = HashMap::new();
|
||||
userdata.insert("desktop-entry".to_string(), serde_json::to_string(params.application)?);
|
||||
userdata.insert("desktop-entry".into(), serde_json::to_string(params.application)?);
|
||||
|
||||
let resolution = Self::calculate_resolution(params.res_mode, params.orientation_mode);
|
||||
|
||||
@@ -432,12 +435,11 @@ impl View {
|
||||
params.auto_start,
|
||||
WvrProcessLaunchParams {
|
||||
env,
|
||||
exec,
|
||||
argv,
|
||||
name: params.application.app_name.to_string(),
|
||||
args,
|
||||
resolution,
|
||||
pos_mode,
|
||||
icon: params.application.icon_path.as_ref().map(|x| x.as_ref().to_string()),
|
||||
icon: params.application.icon_path.as_deref().map(String::from),
|
||||
userdata,
|
||||
},
|
||||
)?;
|
||||
@@ -486,7 +488,7 @@ impl View {
|
||||
pub fn mount_popup(
|
||||
frontend_tasks: FrontendTasks,
|
||||
globals: WguiGlobals,
|
||||
entry: DesktopEntry,
|
||||
entry: Rc<DesktopEntry>,
|
||||
popup: PopupHolder<View>,
|
||||
on_app_pins_changed: Box<dyn Fn()>,
|
||||
pinned_app: Option<PinnedApp>,
|
||||
|
||||
@@ -1082,9 +1082,7 @@ fn apply_subpath(
|
||||
};
|
||||
|
||||
if action_name == "scroll" {
|
||||
*side_mut = Some(OneOrMany::One(format!(
|
||||
"/user/hand/{side_str}/input/{subpath_str}"
|
||||
)));
|
||||
*side_mut = Some(OneOrMany::One(format!("/user/hand/{side_str}/input/{subpath_str}")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{rc::Rc, sync::Arc};
|
||||
|
||||
use crate::testbed::{Testbed, TestbedUpdateParams};
|
||||
use dash_frontend::frontend::{self, FrontendUpdateParams};
|
||||
use wgui::{gfx::WGfx, layout::Layout, theme::WguiTheme};
|
||||
use wgui::{gfx::WGfx, i18n::Locale, layout::Layout, theme::WguiTheme};
|
||||
use wlx_common::{
|
||||
async_executor::AsyncExecutor, dash_interface_emulated::DashInterfaceEmulated,
|
||||
locale::WayVRLangProvider,
|
||||
@@ -15,8 +15,9 @@ pub struct TestbedDashboard {
|
||||
|
||||
impl TestbedDashboard {
|
||||
pub fn new(gfx: Arc<WGfx>) -> anyhow::Result<Self> {
|
||||
let interface = DashInterfaceEmulated::new(gfx);
|
||||
let lang_provider = WayVRLangProvider::default();
|
||||
let locale = Locale::from_env(&lang_provider);
|
||||
let interface = DashInterfaceEmulated::new(gfx, &locale);
|
||||
let palette_name = std::env::var("PALETTE").unwrap_or_else(|_| "Default".to_string());
|
||||
let executor = wlx_common::async_executor::create_local();
|
||||
|
||||
|
||||
@@ -49,9 +49,8 @@ pub enum HandsfreeParams {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WvrProcessLaunchParams {
|
||||
pub name: String,
|
||||
pub exec: String,
|
||||
pub argv: Vec<String>,
|
||||
pub env: Vec<String>,
|
||||
pub args: String,
|
||||
pub icon: Option<String>,
|
||||
pub resolution: [u32; 2],
|
||||
pub pos_mode: PositionMode,
|
||||
|
||||
+22
-19
@@ -6,6 +6,7 @@ use crate::{
|
||||
backend::input::InputState,
|
||||
ipc::{event_queue::SyncEventQueue, signal::WayVRSignal},
|
||||
};
|
||||
use anyhow::Context;
|
||||
use bytes::BufMut;
|
||||
use glam::Vec3A;
|
||||
use interprocess::local_socket::{self, ToNsName, traits::Listener};
|
||||
@@ -95,10 +96,6 @@ pub struct TickParams<'a> {
|
||||
pub hid_wrapper: &'a mut HidWrapper,
|
||||
}
|
||||
|
||||
pub fn gen_args_vec(input: &str) -> Vec<&str> {
|
||||
input.split_whitespace().collect()
|
||||
}
|
||||
|
||||
pub fn gen_env_vec(input: &[String]) -> Vec<(&str, &str)> {
|
||||
input
|
||||
.iter()
|
||||
@@ -226,22 +223,28 @@ impl Connection {
|
||||
serial: ipc::Serial,
|
||||
packet_params: packet_client::WvrProcessLaunchParams,
|
||||
) -> anyhow::Result<()> {
|
||||
let args_vec = gen_args_vec(&packet_params.args);
|
||||
let env_vec = gen_env_vec(&packet_params.env);
|
||||
let res = {
|
||||
let mut argv_iter = packet_params.argv.iter().map(String::as_str);
|
||||
let exec_path = argv_iter
|
||||
.next()
|
||||
.context("No executable for process_launch")?;
|
||||
let exec_args = argv_iter.collect::<Box<_>>();
|
||||
let env_vec = gen_env_vec(&packet_params.env);
|
||||
|
||||
let res = params.wvr_server.spawn_process(
|
||||
&packet_params.name,
|
||||
&packet_params.exec,
|
||||
&args_vec,
|
||||
&env_vec,
|
||||
packet_params.resolution,
|
||||
packet_params.pos_mode,
|
||||
None,
|
||||
packet_params.icon.as_deref(),
|
||||
packet_params.userdata,
|
||||
);
|
||||
|
||||
let res = res.map(ProcessHandle::as_packet).map_err(|e| e.to_string());
|
||||
params.wvr_server.spawn_process(
|
||||
&packet_params.name,
|
||||
exec_path,
|
||||
&exec_args,
|
||||
&env_vec,
|
||||
packet_params.resolution,
|
||||
packet_params.pos_mode,
|
||||
None,
|
||||
packet_params.icon.as_deref(),
|
||||
packet_params.userdata,
|
||||
)
|
||||
}
|
||||
.map(ProcessHandle::as_packet)
|
||||
.map_err(|e| e.to_string());
|
||||
|
||||
send_packet(
|
||||
&mut self.conn,
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams};
|
||||
use glam::{Affine2, Affine3A, Quat, Vec2, vec2, vec3};
|
||||
use wayvr_ipc::{
|
||||
@@ -43,7 +44,7 @@ use crate::{
|
||||
},
|
||||
},
|
||||
config::{none_if_0, save_settings},
|
||||
ipc::ipc_server::{gen_args_vec, gen_env_vec},
|
||||
ipc::ipc_server::gen_env_vec,
|
||||
state::AppState,
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
@@ -440,7 +441,11 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
||||
) -> anyhow::Result<WvrProcessHandle> {
|
||||
let wvr_server = app.wvr_server.as_mut().unwrap();
|
||||
|
||||
let args_vec = gen_args_vec(¶ms.args);
|
||||
let mut argv_iter = params.argv.iter().map(String::as_str);
|
||||
let exec_path = argv_iter
|
||||
.next()
|
||||
.context("No executable for process_launch")?;
|
||||
let exec_args = argv_iter.collect::<Box<_>>();
|
||||
let env_vec = gen_env_vec(¶ms.env);
|
||||
|
||||
if auto_start {
|
||||
@@ -451,8 +456,8 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
||||
wvr_server
|
||||
.spawn_process(
|
||||
¶ms.name,
|
||||
¶ms.exec,
|
||||
&args_vec,
|
||||
exec_path,
|
||||
&exec_args,
|
||||
&env_vec,
|
||||
params.resolution,
|
||||
params.pos_mode,
|
||||
|
||||
+1
-1
@@ -179,7 +179,7 @@ impl AppState {
|
||||
|
||||
let ipc_server = ipc_server::WayVRServer::new()?;
|
||||
|
||||
let mut desktop_finder = DesktopFinder::new();
|
||||
let mut desktop_finder = DesktopFinder::new(params.wgui_globals.i18n().get_locale());
|
||||
desktop_finder.refresh();
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ clap.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
shell-words.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
env_logger = "0.11.11"
|
||||
shell-words = "1.1.1"
|
||||
@@ -107,13 +107,12 @@ pub async fn wvr_process_terminate(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn wvr_process_launch(
|
||||
state: &mut WayVRClientState,
|
||||
exec: String,
|
||||
argv: Vec<String>,
|
||||
name: String,
|
||||
env: Vec<String>,
|
||||
resolution: [u32; 2],
|
||||
pos_mode: PositionMode,
|
||||
icon: Option<String>,
|
||||
args: String,
|
||||
userdata: HashMap<String, String>,
|
||||
) {
|
||||
handle_result(
|
||||
@@ -123,9 +122,8 @@ pub async fn wvr_process_launch(
|
||||
state.serial_generator.increment_get(),
|
||||
packet_client::WvrProcessLaunchParams {
|
||||
env,
|
||||
exec,
|
||||
argv,
|
||||
name,
|
||||
args,
|
||||
resolution,
|
||||
pos_mode,
|
||||
icon,
|
||||
|
||||
+8
-10
@@ -157,13 +157,12 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
|
||||
wvr_process_terminate(state, handle).await;
|
||||
}
|
||||
Subcommands::ProcessLaunch {
|
||||
exec,
|
||||
name,
|
||||
env,
|
||||
resolution,
|
||||
pos,
|
||||
icon,
|
||||
args,
|
||||
command,
|
||||
} => {
|
||||
let env = env.split(",").map(|s| s.to_string()).collect::<Vec<_>>();
|
||||
let resolution = resolution
|
||||
@@ -181,13 +180,12 @@ async fn run_once(state: &mut WayVRClientState, args: Args) -> anyhow::Result<()
|
||||
|
||||
wvr_process_launch(
|
||||
state,
|
||||
exec,
|
||||
command,
|
||||
name,
|
||||
env,
|
||||
resolution,
|
||||
pos_mode,
|
||||
icon,
|
||||
args,
|
||||
HashMap::new(),
|
||||
)
|
||||
.await;
|
||||
@@ -316,17 +314,17 @@ enum Subcommands {
|
||||
/// Environment variables, separated by comma
|
||||
#[arg(short, long, default_value = "")]
|
||||
env: String,
|
||||
/// Executable to run
|
||||
exec: String,
|
||||
#[arg(default_value = "1920x1080")]
|
||||
#[arg(short, long, default_value = "1920x1080")]
|
||||
resolution: String,
|
||||
/// Default positioning
|
||||
#[arg(short, long, default_value = "anchored")]
|
||||
pos: PosModeEnum,
|
||||
/// Absolute path to the app icon
|
||||
#[arg(short, long, default_value = "")]
|
||||
icon: Option<String>,
|
||||
/// Arguments to pass to executable
|
||||
#[arg(default_value = "")]
|
||||
args: String,
|
||||
/// Executable and optional arguments
|
||||
#[arg(required = true, num_args = 1..)]
|
||||
command: Vec<String>,
|
||||
},
|
||||
/// Trigger haptics on the user's controller
|
||||
Haptics {
|
||||
|
||||
@@ -69,6 +69,16 @@ pub trait LangsList {
|
||||
}
|
||||
|
||||
impl Locale {
|
||||
#[must_use]
|
||||
/// Returns a clone of `self` without `region`
|
||||
pub fn without_region(&self) -> Locale {
|
||||
Locale {
|
||||
lang: self.lang.clone(),
|
||||
region: None,
|
||||
matched: self.matched.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn match_locale<'o>(
|
||||
default_lang: &'static str,
|
||||
lang: &str,
|
||||
|
||||
@@ -24,6 +24,7 @@ smol.workspace = true
|
||||
strum.workspace = true
|
||||
xdg.workspace = true
|
||||
|
||||
freedesktop-desktop-entry = "0.8.4"
|
||||
glob = "0.3.3"
|
||||
identicons-svg = "0.1.0"
|
||||
rodio = {
|
||||
@@ -31,6 +32,5 @@ rodio = {
|
||||
default-features = false,
|
||||
features = ["hound", "mp3", "playback", "recording"]
|
||||
}
|
||||
rust-ini = "0.21.3"
|
||||
walkdir = "2.5.0"
|
||||
wgui = { path = "../wgui/" }
|
||||
@@ -5,7 +5,7 @@ use wayvr_ipc::{
|
||||
packet_client::WvrProcessLaunchParams,
|
||||
packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle},
|
||||
};
|
||||
use wgui::gfx::WGfx;
|
||||
use wgui::{gfx::WGfx, i18n::Locale};
|
||||
|
||||
use crate::{
|
||||
config::GeneralConfig,
|
||||
@@ -73,7 +73,7 @@ pub struct DashInterfaceEmulated {
|
||||
}
|
||||
|
||||
impl DashInterfaceEmulated {
|
||||
pub fn new(gfx: Arc<WGfx>) -> Self {
|
||||
pub fn new(gfx: Arc<WGfx>, locale: &Locale) -> Self {
|
||||
let mut processes = DenseSlotMap::<EmuProcessHandle, EmuProcess>::default();
|
||||
let process_handle = processes.insert(EmuProcess {
|
||||
name: String::from("My app"),
|
||||
@@ -85,7 +85,7 @@ impl DashInterfaceEmulated {
|
||||
visible: true,
|
||||
});
|
||||
|
||||
let mut desktop_finder = DesktopFinder::new();
|
||||
let mut desktop_finder = DesktopFinder::new(locale);
|
||||
desktop_finder.refresh();
|
||||
|
||||
// Use serde defaults
|
||||
|
||||
@@ -10,37 +10,35 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::cache_dir;
|
||||
use crate::{cache_dir, locale::Language};
|
||||
use anyhow::Context;
|
||||
use ini::Ini;
|
||||
use freedesktop_desktop_entry::DesktopEntry as FreedesktopDesktopEntry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use walkdir::WalkDir;
|
||||
use wgui::i18n::Locale;
|
||||
|
||||
struct DesktopEntryOwned {
|
||||
app_id: String,
|
||||
exec_path: String,
|
||||
exec_args: String,
|
||||
app_name: String,
|
||||
icon_path: Option<String>,
|
||||
categories: Vec<String>,
|
||||
app_id: Box<str>,
|
||||
exec_argv: Box<[Box<str>]>,
|
||||
app_name: Box<str>,
|
||||
icon_path: Option<Box<str>>,
|
||||
categories: Box<[Box<str>]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DesktopEntry {
|
||||
pub exec_path: Rc<str>,
|
||||
pub exec_args: Rc<str>,
|
||||
pub exec_argv: Rc<[Rc<str>]>,
|
||||
pub app_id: Rc<str>,
|
||||
pub app_name: Rc<str>,
|
||||
pub icon_path: Option<Rc<str>>,
|
||||
pub categories: Vec<Rc<str>>,
|
||||
pub categories: Rc<[Rc<str>]>,
|
||||
}
|
||||
|
||||
impl From<DesktopEntryOwned> for DesktopEntry {
|
||||
fn from(value: DesktopEntryOwned) -> Self {
|
||||
Self {
|
||||
app_id: value.app_id.into(),
|
||||
exec_path: value.exec_path.into(),
|
||||
exec_args: value.exec_args.into(),
|
||||
exec_argv: value.exec_argv.into_iter().map(|x| x.into()).collect(),
|
||||
app_name: value.app_name.into(),
|
||||
icon_path: value.icon_path.map(|x| x.into()),
|
||||
categories: value.categories.into_iter().map(|x| x.into()).collect(),
|
||||
@@ -60,17 +58,18 @@ struct DesktopFinderParams {
|
||||
size_preferences: Vec<&'static OsStr>,
|
||||
icon_folders: Vec<String>,
|
||||
app_folders: Vec<String>,
|
||||
langs: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct DesktopFinder {
|
||||
params: Arc<DesktopFinderParams>,
|
||||
entry_cache: HashMap<String, DesktopEntry>,
|
||||
entry_cache: HashMap<String, Rc<DesktopEntry>>,
|
||||
bg_task: Option<JoinHandle<HashMap<String, DesktopEntryOwned>>>,
|
||||
}
|
||||
|
||||
impl DesktopFinder {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
pub fn new(locale: &Locale) -> Self {
|
||||
let xdg = xdg::BaseDirectories::new();
|
||||
|
||||
let mut app_folders = vec![];
|
||||
@@ -107,11 +106,21 @@ impl DesktopFinder {
|
||||
.map(OsStr::new)
|
||||
.collect();
|
||||
|
||||
let langs = {
|
||||
let main_lang = locale.without_region().to_string();
|
||||
if main_lang == Language::get_default().code() {
|
||||
vec![main_lang]
|
||||
} else {
|
||||
vec![main_lang, Language::get_default().code().to_string()]
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
params: Arc::new(DesktopFinderParams {
|
||||
app_folders,
|
||||
icon_folders,
|
||||
size_preferences,
|
||||
langs,
|
||||
}),
|
||||
entry_cache: HashMap::new(),
|
||||
bg_task: None,
|
||||
@@ -146,79 +155,53 @@ impl DesktopFinder {
|
||||
}
|
||||
|
||||
let file_name = entry.file_name().to_string_lossy();
|
||||
let Some(app_id) = Path::new(entry.file_name())
|
||||
.file_stem()
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let file_path = entry.path().display().to_string();
|
||||
|
||||
if known_files.contains(file_name.as_ref()) {
|
||||
// as per xdg spec, user entries of the same filename will override system entries
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = format!("{}/{}", path, file_name);
|
||||
|
||||
let ini = match Ini::load_from_file(&file_path) {
|
||||
Ok(ini) => ini,
|
||||
let desktop = match FreedesktopDesktopEntry::from_path(entry.path(), Some(¶ms.langs)) {
|
||||
Ok(desktop) => desktop,
|
||||
Err(e) => {
|
||||
log::debug!("Failed to read INI for .desktop file {}: {:?}, skipping", file_path, e);
|
||||
log::debug!("Failed to read .desktop file {}: {:?}, skipping", file_path, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let Some(section) = ini.section(Some("Desktop Entry")) else {
|
||||
if desktop.groups.desktop_entry().is_none() {
|
||||
log::debug!("Failed to get [Desktop Entry] section for file {}, skipping", file_path);
|
||||
continue;
|
||||
};
|
||||
|
||||
if section.contains_key("OnlyShowIn") {
|
||||
continue; // probably XFCE, KDE, GNOME or other DE-specific stuff
|
||||
}
|
||||
|
||||
if let Some(x) = section.get("Terminal")
|
||||
&& x == "true"
|
||||
{
|
||||
// prefer entries from earlier data directories
|
||||
let app_id = desktop.id();
|
||||
if known_files.contains(app_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(x) = section.get("NoDisplay")
|
||||
&& x.eq_ignore_ascii_case("true")
|
||||
{
|
||||
continue; // This application is hidden
|
||||
if desktop.only_show_in().is_some() || desktop.terminal() || desktop.no_display() || desktop.hidden() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(x) = section.get("Hidden")
|
||||
&& x.eq_ignore_ascii_case("true")
|
||||
{
|
||||
continue; // This application is hidden
|
||||
}
|
||||
|
||||
let Some(exec) = section.get("Exec") else {
|
||||
let Some(exec) = desktop.exec() else {
|
||||
log::debug!("Failed to get desktop entry Exec for file {}, skipping", file_path);
|
||||
continue;
|
||||
};
|
||||
|
||||
for entry in &CMD_BLOCKLIST {
|
||||
if exec.contains(entry) {
|
||||
continue 'entries;
|
||||
}
|
||||
if CMD_BLOCKLIST.iter().any(|blocked| exec.contains(blocked)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (exec_path, exec_args) = match exec.split_once(" ") {
|
||||
Some((left, right)) => (
|
||||
left,
|
||||
right
|
||||
.split(" ")
|
||||
.filter(|arg| !arg.starts_with('%')) // exclude arguments like "%f"
|
||||
.map(String::from)
|
||||
.collect(),
|
||||
),
|
||||
None => (exec, Vec::new()),
|
||||
let exec_argv = desktop
|
||||
.parse_exec_with_uris(&[], ¶ms.langs)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|x| x.into_boxed_str())
|
||||
.collect::<Box<[_]>>();
|
||||
|
||||
if exec_argv.is_empty() {
|
||||
log::debug!("Invalid Exec in desktop file {}, skipping", file_path);
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(app_name) = section.get("Name") else {
|
||||
let Some(app_name) = desktop.name(¶ms.langs) else {
|
||||
log::debug!(
|
||||
"Failed to get desktop entry application name for file {}, skipping",
|
||||
file_path
|
||||
@@ -226,33 +209,31 @@ impl DesktopFinder {
|
||||
continue;
|
||||
};
|
||||
|
||||
let icon_path = section
|
||||
.get("Icon")
|
||||
let icon_path = desktop
|
||||
.icon()
|
||||
.and_then(|icon_name| Self::find_icon(¶ms, icon_name))
|
||||
.or_else(|| Self::create_icon(&file_name).ok());
|
||||
|
||||
let mut vec_categories = Vec::<String>::new();
|
||||
|
||||
if let Some(categories) = section.get("Categories") {
|
||||
for cat in categories.split(";") {
|
||||
if CATEGORY_TYPE_BLOCKLIST.contains(&cat) {
|
||||
continue 'entries;
|
||||
}
|
||||
vec_categories.push(String::from(cat));
|
||||
let mut vec_categories = Vec::<Box<str>>::new();
|
||||
for cat in desktop.categories().unwrap_or_default() {
|
||||
if CATEGORY_TYPE_BLOCKLIST.contains(&cat) {
|
||||
continue 'entries;
|
||||
}
|
||||
if !cat.is_empty() {
|
||||
vec_categories.push(cat.into());
|
||||
}
|
||||
}
|
||||
|
||||
known_files.insert(file_name.to_string());
|
||||
known_files.insert(app_id.to_owned());
|
||||
|
||||
entries.insert(
|
||||
app_id.clone(),
|
||||
app_id.to_owned(),
|
||||
DesktopEntryOwned {
|
||||
app_id,
|
||||
app_name: String::from(app_name),
|
||||
exec_path: String::from(exec_path),
|
||||
exec_args: exec_args.join(" "),
|
||||
icon_path,
|
||||
categories: vec_categories,
|
||||
app_id: app_id.into(),
|
||||
app_name: app_name.into(),
|
||||
exec_argv,
|
||||
icon_path: icon_path.map(|i| i.into_boxed_str()),
|
||||
categories: vec_categories.into_boxed_slice(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -331,15 +312,15 @@ impl DesktopFinder {
|
||||
|
||||
self.entry_cache.clear();
|
||||
for (app_id, entry) in entries {
|
||||
self.entry_cache.insert(app_id, entry.into());
|
||||
self.entry_cache.insert(app_id, Rc::new(entry.into()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cached_entry(&self, app_id: &str) -> Option<&DesktopEntry> {
|
||||
pub fn get_cached_entry(&self, app_id: &str) -> Option<&Rc<DesktopEntry>> {
|
||||
self.entry_cache.get(app_id)
|
||||
}
|
||||
|
||||
pub fn find_entries(&mut self) -> HashMap<String, DesktopEntry> {
|
||||
pub fn find_entries(&mut self) -> HashMap<String, Rc<DesktopEntry>> {
|
||||
self.wait_for_entries();
|
||||
self.entry_cache.clone()
|
||||
}
|
||||
|
||||
@@ -95,10 +95,10 @@ pub struct OpenXrInputProfile {
|
||||
fn strip_axis_component(path: &str) -> String {
|
||||
if let Some((prefix, suffix)) = path.rsplit_once('/')
|
||||
&& (suffix == "x" || suffix == "y")
|
||||
&& (prefix.ends_with("/thumbstick") || prefix.ends_with("/trackpad") || prefix.ends_with("/joystick"))
|
||||
{
|
||||
return prefix.to_owned();
|
||||
}
|
||||
&& (prefix.ends_with("/thumbstick") || prefix.ends_with("/trackpad") || prefix.ends_with("/joystick"))
|
||||
{
|
||||
return prefix.to_owned();
|
||||
}
|
||||
path.to_owned()
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user