Compare commits

...
Author SHA1 Message Date
galister 89eb2e04f5 add spirv-headers to ci 2026-10-28 06:00:02 +09:00
galister 12253a5e20 bump whisper-rs 2026-10-28 05:53:43 +09:00
galister 9e116988ac wvr_server: process matching tweaks 2026-10-28 05:26:14 +09:00
Aleksander 466960d32a Add contribution instructions regarding LLM usage policy [skip ci] 2026-10-27 12:13:34 +09:00
10 changed files with 135 additions and 95 deletions

No files matched your search

@@ -22,7 +22,7 @@ sudo apt-get update
sudo apt-get install -y fuse cmake pkg-config fontconfig libasound2-dev libxkbcommon-dev libxkbcommon-x11-0 libxkbcommon-x11-dev \
libopenxr-dev libfontconfig-dev libdbus-1-dev libpipewire-0.3-0 libpipewire-0.3-dev libspa-0.2-dev libx11-6 libxext6 libxrandr2 \
libx11-dev libxext-dev libxrandr-dev libopenvr-dev libopenvr-api1t64 libwayland-dev libegl-dev libxcb-glx0 libxcb-glx0-dev \
dav1d libdav1d-dev libvulkan-dev glslc vulkan-tools libudev-dev libudev1 libinput-dev libinput10
dav1d libdav1d-dev libvulkan-dev glslc vulkan-tools libudev-dev libudev1 libinput-dev libinput10 spirv-headers
rustup update
+46
View File
@@ -0,0 +1,46 @@
# Contributing
Contributions are warmly welcome. Before you start, please read the entire document carefully.
## AI Policy
Do not use LLMs to generate substantial pieces of code. You need to fully understand every line of code you change. No exceptions.
Commits, issues and pull requests must be made by humans. Co-authoring by AI is not allowed.
Looking to create an issue on the project, such as a feature request or bug report? Focus on writing what you know for sure in simple terms. We'll figure it out from there.
Usage of Gen-AI for visuals and audio is prohibited.
The only exception is LLM-translated strings generated by the scripts/translator tool.
## Basics
Before committing, make sure to format your Rust code by running:
```
cargo fmt
```
You should also check for any compiler warnings too:
```
cargo check
```
or even better:
```
cargo clippy
```
## Large contributions
If you want to submit a relatively big pull requests, it's highly advisable to create a separate issue request for it. That way we can discuss the approach and sort out the details first.
## Adding translations
Look at `scripts/translator/run.sh` for more information.
## Licensing
WayVR is developed and published under [GPL-3 License](https://www.gnu.org/licenses/gpl-3.0.en.html).
Generated
+2 -4
View File
@@ -6374,8 +6374,7 @@ dependencies = [
[[package]]
name = "whisper-rs"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2088172d00f936c348d6a72f488dc2660ab3f507263a195df308a3c2383229f6"
source = "git+https://github.com/wayvr-org/whisper-rs.git?rev=2d908ec36cd41521a28f7f5fb5dc95499fd40051#2d908ec36cd41521a28f7f5fb5dc95499fd40051"
dependencies = [
"libc",
"whisper-rs-sys",
@@ -6384,8 +6383,7 @@ dependencies = [
[[package]]
name = "whisper-rs-sys"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6986c0fe081241d391f09b9a071fbcbb59720c3563628c3c829057cf69f2a56f"
source = "git+https://github.com/wayvr-org/whisper-rs.git?rev=2d908ec36cd41521a28f7f5fb5dc95499fd40051#2d908ec36cd41521a28f7f5fb5dc95499fd40051"
dependencies = [
"bindgen",
"cfg-if",
+33 -4
View File
@@ -1,6 +1,32 @@
#!/bin/bash
# Do not run this script if you don't have at least 32 GiB of system memory.
# This is the translation tool intended for machine-translating strings.
#
# ###########################
# # Adding new translations #
# ###########################
#
# If you want to add a new language, follow these steps:
#
# 1. Include the language in `wlx-common/src/locale.rs`:
# - add your entry in `enum Language`
# - add your entry in Language::code() function
# - add your language code in Language::add_codes() function
#
# 2. Create a new template file
# - Add your language code in `scripts/translator/templates/<lang code>.json`
# (you can copy one of nearby files and adapt them accordingly)
#
# 3. Modify this very file
# - Go to the bottom and add your language code entry here too
#
# 4. Run the script (it will hint you which LANG_PATHs are supported)
#
# ############################################
# # Running machine translations (local LLM) #
# ############################################
#
# Do not run this script if you don't have at least ~16 GiB of free system memory.
# Submitting PRs that lack translated strings is okay if you don't meet the
# system requirements to run this script, or if you simply prefer not to; we are
# regularly updating the missing translation strings anyway.
@@ -8,15 +34,18 @@
# Base language: English (en.json)
#
# This tool expects llama.cpp server being present (llama-server) at port 8080.
#
# Currently we're using Gemma 4 for machine translations.
# Model URL:
# https://huggingface.co/unsloth/gemma-4-31B-it-GGUF
# Recommended quantization: Q4_K_M
#
set -e
cd "$(dirname "$0")"
bun install
# model URL:
# https://huggingface.co/unsloth/gemma-4-31B-it-GGUF
# you can specify your own if you want to.
if [ -z "${MODEL}" ]; then
export MODEL="gemma-4-31B-it-Q4_K_M"
fi
+7 -1
View File
@@ -109,7 +109,13 @@ smithay-clipboard = "0.7.3"
sysinfo = { version = "0.39.6" }
thiserror = "2.0.18"
tracing = "0.1.44"
whisper-rs = { version = "0.16.0", features = ["vulkan"], optional = true }
whisper-rs = {
# version = "0.16.0",
git = "https://github.com/wayvr-org/whisper-rs.git",
rev = "2d908ec36cd41521a28f7f5fb5dc95499fd40051",
features = ["vulkan"],
optional = true
}
xcb = {
version = "1.7.0",
features = [
+15 -16
View File
@@ -21,7 +21,7 @@ use smithay::{
use wgui::log::LogErr;
use xkbcommon::xkb;
use crate::backend::wayvr::{ExternalProcessRequest, WayVRTask};
use crate::backend::wayvr::WayVRTask;
use super::{
ProcessWayVREnv,
@@ -32,6 +32,7 @@ use super::{
pub struct WayVRClient {
pub client: wayland_server::Client,
pub pid: u32,
pub process: process::ProcessHandle,
}
pub struct WayVRCompositor {
@@ -160,16 +161,17 @@ impl WayVRCompositor {
let process_env = get_wayvr_env_from_pid(credentials.pid)?;
// Find suitable auth key from the process list
for (_, process) in processes {
if let process::Process::Managed(process) = &process
for (handle, process) in processes.iter() {
if let process::Process::Managed(process) = process
&& let Some(auth_key) = &process_env.display_auth
{
// Find process with matching auth key
if process.auth_key.as_str() == auth_key {
// Add client
// Associate client now, instead of trying to match env later
self.add_client(WayVRClient {
client,
pid: credentials.pid as u32,
process: handle,
});
return Ok(());
}
@@ -178,18 +180,15 @@ impl WayVRCompositor {
// This is a new process which we didn't met before.
// Treat external processes exclusively (spawned by the user or external program)
log::warn!(
"External process ID {} connected to this Wayland server",
credentials.pid
);
self.state
.wayvr_tasks
.send(WayVRTask::NewExternalProcess(ExternalProcessRequest {
env: process_env,
client,
pid: credentials.pid as u32,
}));
let pid = credentials.pid as u32;
log::warn!("External process ID {pid} connected to this Wayland server");
let process =
processes.insert(process::Process::External(process::ExternalProcess { pid }));
self.add_client(WayVRClient {
client,
pid,
process,
});
Ok(())
}
+16 -35
View File
@@ -118,14 +118,6 @@ pub struct ProcessWayVREnv {
pub display_name: Option<String>, // Externally spawned process by a user script
}
#[derive(Clone)]
pub struct ExternalProcessRequest {
#[allow(dead_code)]
pub env: ProcessWayVREnv,
pub client: wayland_server::Client,
pub pid: u32,
}
#[derive(Clone)]
pub enum WayVRTask {
NewToplevel(ClientId, ToplevelSurface),
@@ -133,7 +125,6 @@ pub enum WayVRTask {
MinimizeRequest(ClientId, ToplevelSurface),
TitleChange(ClientId, ToplevelSurface),
VisibilityChange(window::WindowHandle, bool),
NewExternalProcess(ExternalProcessRequest),
ProcessTerminationRequest(process::ProcessHandle, KillSignal),
CloseWindowRequest(window::WindowHandle),
DndStarted {
@@ -173,10 +164,6 @@ pub enum PointerFocusTarget {
None,
}
pub enum TickTask {
NewExternalProcess(ExternalProcessRequest), // Call WayVRCompositor::add_client after receiving this message
}
const KEY_REPEAT_DELAY: i32 = 500;
const KEY_REPEAT_RATE: i32 = 50;
const WAYVR_SCREEN_RES: [i32; 2] = [2560, 1440];
@@ -325,26 +312,29 @@ impl WvrServerState {
#[allow(clippy::too_many_lines)]
#[allow(clippy::significant_drop_tightening)]
pub fn tick_events(app: &mut AppState) -> anyhow::Result<Vec<TickTask>> {
let mut tasks: Vec<TickTask> = Vec::new();
pub fn tick_events(app: &mut AppState) -> anyhow::Result<()> {
let Some(wvr_server) = app.wvr_server.as_mut() else {
return Ok(tasks);
return Ok(());
};
app.ipc_server.tick(&mut ipc_server::TickParams {
wvr_server,
input_state: &app.input_state,
tasks: &mut tasks,
signals: &app.wayvr_signals,
hid_wrapper: &mut app.hid_provider,
});
// Tick all child processes
// Keep launch records while wl client is attached
let attached_processes: HashSet<_> = wvr_server
.manager
.clients
.iter()
.map(|client| client.process)
.collect();
let mut to_remove: SmallVec<[process::ProcessHandle; 2]> = SmallVec::new();
for (handle, process) in &mut wvr_server.processes {
if !process.is_running() {
if !process.is_running() && !attached_processes.contains(&handle) {
to_remove.push(handle);
}
}
@@ -356,9 +346,6 @@ impl WvrServerState {
while let Some(task) = wvr_server.tasks.read() {
match task {
WayVRTask::NewExternalProcess(req) => {
tasks.push(TickTask::NewExternalProcess(req));
}
WayVRTask::NewToplevel(client_id, toplevel) => {
let toplevel = Rc::new(toplevel);
@@ -368,15 +355,14 @@ impl WvrServerState {
continue;
}
let Some(process_handle) =
process::find_by_pid(&wvr_server.processes, client.pid)
else {
let process_handle = client.process;
if !wvr_server.processes.contains_key(process_handle) {
log::error!(
"WayVR window creation failed: Unexpected process ID {}. It wasn't registered before.",
"WayVR window creation failed: Process for PID {} was removed after the client connected.",
client.pid
);
continue;
};
}
let output_bounds = wvr_server.manager.state.output_logical_size();
@@ -601,7 +587,7 @@ impl WvrServerState {
}
let Some(window_handle) = wvr_server.wm.find_window_handle(&toplevel)
else {
log::warn!("MinimizeRequest: Couldn't find matching window handle");
log::warn!("TitleChange: Couldn't find matching window handle");
continue;
};
if let Some(oid) = wvr_server.window_to_overlay.get(&window_handle) {
@@ -703,7 +689,7 @@ impl WvrServerState {
wvr_server.ticks += 1;
Ok(tasks)
Ok(())
}
pub fn config_changed(&mut self, config: &GeneralConfig) {
@@ -1307,11 +1293,6 @@ impl WvrServerState {
self.manager.state.set_clipboard_text(content);
}
pub fn add_external_process(&mut self, pid: u32) -> process::ProcessHandle {
self.processes
.insert(process::Process::External(process::ExternalProcess { pid }))
}
#[allow(clippy::too_many_arguments)]
pub fn spawn_process(
&mut self,
+13 -9
View File
@@ -87,12 +87,14 @@ impl Process {
impl Drop for WayVRProcess {
fn drop(&mut self) {
log::info!(
"Sending SIGTERM (graceful exit) to process {} ({})",
self.child.id(),
self.exec_path.as_str()
);
self.kill(libc::SIGTERM);
if self.is_running() {
log::info!(
"Sending SIGTERM (graceful exit) to process {} ({})",
self.child.id(),
self.exec_path.as_str()
);
self.kill(libc::SIGTERM);
}
}
}
@@ -127,9 +129,11 @@ impl WayVRProcess {
}
fn kill(&mut self, signal: i32) {
unsafe {
// Gracefully stop process
libc::kill(self.child.id() as i32, signal);
if self.is_running() {
unsafe {
// Gracefully stop process
libc::kill(self.child.id() as i32, signal);
}
}
}
+2 -23
View File
@@ -1,6 +1,6 @@
use std::sync::Arc;
use crate::backend::wayvr::{self, WayVRTask, WvrServerState};
use crate::backend::wayvr::{WayVRTask, WvrServerState};
use wayvr_ipc::packet_client::{HandsfreeMode, HandsfreeParams};
use wayvr_ipc::packet_server::PacketServer;
use wlx_common::config::HandsfreePointer;
@@ -15,24 +15,6 @@ use crate::{
windowing::{OverlaySelector, manager::OverlayWindowManager},
};
fn process_tick_tasks(
tick_tasks: Vec<backend::wayvr::TickTask>,
server_state: &mut WvrServerState,
) {
for tick_task in tick_tasks {
match tick_task {
backend::wayvr::TickTask::NewExternalProcess(request) => {
log::info!("Registering external process with PID {}", request.pid);
server_state.add_external_process(request.pid);
server_state.manager.add_client(wayvr::client::WayVRClient {
client: request.client,
pid: request.pid,
});
}
}
}
}
pub fn tick_events<O>(
app: &mut AppState,
overlays: &mut OverlayWindowManager<O>,
@@ -135,10 +117,7 @@ where
}
}
let tick_tasks = WvrServerState::tick_events(app)?;
if let Some(wayvr_server) = app.wvr_server.as_mut() {
process_tick_tasks(tick_tasks, wayvr_server);
}
WvrServerState::tick_events(app)?;
Ok(())
}
-2
View File
@@ -89,8 +89,6 @@ fn read_payload(conn: &mut local_socket::Stream, size: u32) -> anyhow::Result<Op
pub struct TickParams<'a> {
pub wvr_server: &'a mut WvrServerState,
#[allow(dead_code)]
pub tasks: &'a mut Vec<wayvr::TickTask>,
pub signals: &'a SyncEventQueue<WayVRSignal>,
pub input_state: &'a InputState,
pub hid_wrapper: &'a mut HidWrapper,