mirror of
https://github.com/wayvr-org/wayvr.git
synced 2026-10-28 17:06:46 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82374a60a0 | ||
|
|
0d41bf1a3e | ||
|
|
3907e089de | ||
|
|
72bac6465e | ||
|
|
271e037692 | ||
|
|
bc78450c6a | ||
|
|
dce634edd1 | ||
|
|
69d14eebe5 | ||
|
|
54dfb297ac | ||
|
|
1d1230b1e7 | ||
|
|
67b6b90158 | ||
|
|
31ec035db7 | ||
|
|
deb43f0d94 | ||
|
|
4434d82981 | ||
|
|
8ba5d68e5a |
No files matched your search
Generated
+409
-494
File diff suppressed because it is too large.
Load diff
+5
-5
@@ -4,7 +4,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "wlx-overlay-s"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -27,9 +27,9 @@ idmap = "0.2.21"
|
||||
idmap-derive = "0.1.2"
|
||||
input-linux = "0.6.0"
|
||||
json = { version = "0.12.4", optional = true }
|
||||
libc = "0.2.147"
|
||||
log = "0.4.20"
|
||||
once_cell = "1.18.0"
|
||||
libc = "0.2.153"
|
||||
log = "0.4.21"
|
||||
once_cell = "1.19.0"
|
||||
openxr = { version = "0.17.1", features = ["linked"], optional = true }
|
||||
ovr_overlay = { features = ["ovr_input", "ovr_system"], git = "https://github.com/galister/ovr_overlay_oyasumi", optional = true }
|
||||
regex = "1.9.5"
|
||||
@@ -43,7 +43,7 @@ strum = { version = "0.25.0", features = ["derive"] }
|
||||
thiserror = "1.0.56"
|
||||
vulkano = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano", rev = "94f50f1" }
|
||||
wlx-capture = { git = "https://github.com/galister/wlx-capture", default-features = false }
|
||||
wlx-capture = { git = "https://github.com/galister/wlx-capture", tag = "v0.3.0", default-features = false }
|
||||
xdg = "2.5.2"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -75,6 +75,7 @@ impl LinePool {
|
||||
}),
|
||||
data: OpenVrOverlayData {
|
||||
width: 0.002,
|
||||
override_width: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
@@ -160,6 +161,12 @@ impl LinePool {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_dirty(&mut self) {
|
||||
for data in self.lines.values_mut() {
|
||||
data.state.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StaticRenderer {
|
||||
|
||||
@@ -237,6 +237,7 @@ pub fn openvr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
|
||||
.iter()
|
||||
.any(|p| p.now.show_hide && !p.before.show_hide)
|
||||
{
|
||||
lines.mark_dirty(); // workaround to prevent lines from not showing
|
||||
overlays.show_hide(&mut state);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ pub(super) struct OpenVrOverlayData {
|
||||
pub(super) curvature: f32,
|
||||
pub(super) sort_order: u32,
|
||||
pub(crate) width: f32,
|
||||
pub(super) override_width: bool,
|
||||
pub(super) relative_to: RelativeTo,
|
||||
}
|
||||
|
||||
@@ -233,6 +234,15 @@ impl OverlayData<OpenVrOverlayData> {
|
||||
}
|
||||
|
||||
let dimensions = image.extent();
|
||||
if !self.data.override_width {
|
||||
let new_width = ((dimensions[0] as f32) / (dimensions[1] as f32)).min(1.0);
|
||||
if (new_width - self.data.width).abs() > f32::EPSILON {
|
||||
log::info!("{}: New width {}", self.state.name, new_width);
|
||||
self.data.width = new_width;
|
||||
self.upload_width(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
let format = image.format();
|
||||
|
||||
let mut texture = VRVulkanTextureData_t {
|
||||
|
||||
+17
-23
@@ -5,7 +5,7 @@ use openxr as xr;
|
||||
|
||||
use crate::{
|
||||
backend::input::{Haptics, Pointer},
|
||||
state::AppState,
|
||||
state::{AppSession, AppState},
|
||||
};
|
||||
|
||||
use super::XrState;
|
||||
@@ -98,12 +98,7 @@ impl OpenXrInputSource {
|
||||
xr.session.sync_actions(&[(&self.action_set).into()])?;
|
||||
|
||||
for i in 0..2 {
|
||||
self.hands[i].update(
|
||||
&mut state.input_state.pointers[i],
|
||||
&xr.stage,
|
||||
&xr.session,
|
||||
xr.predicted_display_time,
|
||||
)?;
|
||||
self.hands[i].update(&mut state.input_state.pointers[i], xr, &state.session)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -123,11 +118,10 @@ impl OpenXrHand {
|
||||
pub(super) fn update(
|
||||
&self,
|
||||
pointer: &mut Pointer,
|
||||
stage: &xr::Space,
|
||||
session: &XrSession,
|
||||
time: xr::Time,
|
||||
xr: &XrState,
|
||||
session: &AppSession,
|
||||
) -> Result<(), xr::sys::Result> {
|
||||
let location = self.space.locate(stage, time)?;
|
||||
let location = self.space.locate(&xr.stage, xr.predicted_display_time)?;
|
||||
if location
|
||||
.location_flags
|
||||
.contains(xr::SpaceLocationFlags::ORIENTATION_VALID)
|
||||
@@ -140,46 +134,46 @@ impl OpenXrHand {
|
||||
pointer.now.click = self
|
||||
.source
|
||||
.action_click
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state
|
||||
> 0.7;
|
||||
> session.config.xr_click_sensitivity;
|
||||
|
||||
pointer.now.grab = self
|
||||
.source
|
||||
.action_grab
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state
|
||||
> 0.7;
|
||||
> session.config.xr_grab_sensitivity;
|
||||
|
||||
pointer.now.scroll = self
|
||||
.source
|
||||
.action_scroll
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
pointer.now.alt_click = self
|
||||
.source
|
||||
.action_alt_click
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state
|
||||
> 0.7;
|
||||
> session.config.xr_alt_click_sensitivity;
|
||||
|
||||
pointer.now.show_hide = self
|
||||
.source
|
||||
.action_show_hide
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
pointer.now.click_modifier_right = self
|
||||
.source
|
||||
.action_click_modifier_right
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
pointer.now.click_modifier_middle = self
|
||||
.source
|
||||
.action_click_modifier_middle
|
||||
.state(session, xr::Path::NULL)?
|
||||
.state(&xr.session, xr::Path::NULL)?
|
||||
.current_state;
|
||||
|
||||
Ok(())
|
||||
@@ -380,11 +374,11 @@ fn suggest_bindings(
|
||||
),
|
||||
xr::Binding::new(
|
||||
&hands[0].action_grab,
|
||||
instance.string_to_path("/user/hand/left/input/squeeze/value")?,
|
||||
instance.string_to_path("/user/hand/left/input/squeeze/force")?,
|
||||
),
|
||||
xr::Binding::new(
|
||||
&hands[1].action_grab,
|
||||
instance.string_to_path("/user/hand/right/input/squeeze/value")?,
|
||||
instance.string_to_path("/user/hand/right/input/squeeze/force")?,
|
||||
),
|
||||
xr::Binding::new(
|
||||
&hands[0].action_scroll,
|
||||
|
||||
+14
-5
@@ -161,6 +161,7 @@ impl OverlayState {
|
||||
}
|
||||
|
||||
let scale = self.transform.x_axis.length();
|
||||
self.saved_scale = Some(scale);
|
||||
|
||||
let col_z = (self.transform.translation - hmd.translation).normalize();
|
||||
let col_y = up_dir;
|
||||
@@ -287,10 +288,18 @@ impl InteractionHandler for SplitOverlayBackend {
|
||||
}
|
||||
|
||||
pub fn ui_transform(extent: &[u32; 2]) -> Affine2 {
|
||||
let aspect = extent[0] as f32 / extent[1] as f32;
|
||||
let scale = if aspect < 1.0 {
|
||||
Vec2 {
|
||||
x: 1.0 / aspect,
|
||||
y: -1.0,
|
||||
}
|
||||
} else {
|
||||
Vec2 {
|
||||
x: 1.0,
|
||||
y: -1.0 * aspect,
|
||||
}
|
||||
};
|
||||
let center = Vec2 { x: 0.5, y: 0.5 };
|
||||
Affine2::from_cols(
|
||||
Vec2::X,
|
||||
Vec2::NEG_Y * (extent[0] as f32 / extent[1] as f32),
|
||||
center,
|
||||
)
|
||||
Affine2::from_scale_angle_translation(scale, 0.0, center)
|
||||
}
|
||||
+33
-5
@@ -117,6 +117,15 @@ pub struct GeneralConfig {
|
||||
|
||||
#[serde(default = "def_auto")]
|
||||
pub capture_method: Arc<str>,
|
||||
|
||||
#[serde(default = "def_point7")]
|
||||
pub xr_grab_sensitivity: f32,
|
||||
|
||||
#[serde(default = "def_point7")]
|
||||
pub xr_click_sensitivity: f32,
|
||||
|
||||
#[serde(default = "def_point7")]
|
||||
pub xr_alt_click_sensitivity: f32,
|
||||
}
|
||||
|
||||
impl GeneralConfig {
|
||||
@@ -136,8 +145,8 @@ impl GeneralConfig {
|
||||
}
|
||||
|
||||
fn post_load(&self) {
|
||||
GeneralConfig::sanitize_range("keyboard_scale", self.keyboard_scale, 0.0, 5.0);
|
||||
GeneralConfig::sanitize_range("desktop_view_scale", self.desktop_view_scale, 0.0, 5.0);
|
||||
GeneralConfig::sanitize_range("keyboard_scale", self.keyboard_scale, 0.05, 5.0);
|
||||
GeneralConfig::sanitize_range("desktop_view_scale", self.desktop_view_scale, 0.05, 5.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,12 +201,31 @@ pub fn load_general() -> GeneralConfig {
|
||||
// Add files from conf.d directory
|
||||
let path_conf_d = get_conf_d_path();
|
||||
if let Ok(paths_unsorted) = std::fs::read_dir(path_conf_d) {
|
||||
let mut paths: Vec<_> = paths_unsorted
|
||||
.filter_map(|r| match r {
|
||||
Ok(entry) => Some(entry),
|
||||
Err(e) => {
|
||||
error!("Failed to read conf.d directory: {}", e);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// Sort paths alphabetically
|
||||
let mut paths: Vec<_> = paths_unsorted.map(|r| r.unwrap()).collect(); // TODO safe unwrap?
|
||||
paths.sort_by_key(|dir| dir.path());
|
||||
for path in paths {
|
||||
if !path.file_type().unwrap().is_file() {
|
||||
// TODO safe unwrap?
|
||||
let file_type = match path.file_type() {
|
||||
Ok(file_type) => file_type,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to get file type of {}: {}",
|
||||
path.path().to_string_lossy(),
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if !file_type.is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+19
-14
@@ -106,16 +106,14 @@ impl FontCache {
|
||||
}
|
||||
|
||||
let pattern_str = format!("{PRIMARY_FONT}-{size}:style=bold:charset={cp:04x}");
|
||||
|
||||
let mut pattern =
|
||||
OwnedPattern::from_str(&pattern_str).expect("Failed to create fontconfig pattern");
|
||||
let mut pattern = OwnedPattern::from_str(&pattern_str).unwrap(); // safe because PRIMARY_FONT is const
|
||||
self.fc
|
||||
.substitute(&mut pattern, fontconfig::MatchKind::Pattern);
|
||||
pattern.default_substitute();
|
||||
|
||||
let pattern = pattern.font_match(&mut self.fc);
|
||||
|
||||
if let Some(path) = pattern.filename() {
|
||||
for path in pattern.filename().iter() {
|
||||
log::debug!(
|
||||
"Loading font: {} {}pt",
|
||||
pattern.name().unwrap_or(path),
|
||||
@@ -124,12 +122,20 @@ impl FontCache {
|
||||
|
||||
let font_idx = pattern.face_index().unwrap_or(0);
|
||||
|
||||
let face = self
|
||||
.ft
|
||||
.new_face(path, font_idx as _)
|
||||
.expect("Failed to load font face");
|
||||
face.set_char_size(size << 6, size << 6, 96, 96)
|
||||
.expect("Failed to set font size");
|
||||
let face = match self.ft.new_face(path, font_idx as _) {
|
||||
Ok(face) => face,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load font at {}: {:?}", path, e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
match face.set_char_size(size << 6, size << 6, 96, 96) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to set font size: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let idx = coll.fonts.len();
|
||||
for cp in 0..0xFFFF {
|
||||
@@ -156,11 +162,10 @@ impl FontCache {
|
||||
let font = Font { face, glyphs };
|
||||
coll.fonts.push(font);
|
||||
|
||||
idx
|
||||
} else {
|
||||
coll.cp_map.insert(cp, 0);
|
||||
0
|
||||
return idx;
|
||||
}
|
||||
coll.cp_map.insert(cp, 0);
|
||||
0
|
||||
}
|
||||
|
||||
fn get_glyph_for_cp(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
io::Cursor,
|
||||
ops::Add,
|
||||
process::{self, Child},
|
||||
sync::Arc,
|
||||
@@ -8,7 +7,6 @@ use std::{
|
||||
};
|
||||
|
||||
use glam::{Quat, Vec3A};
|
||||
use rodio::{Decoder, Source};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
@@ -613,11 +611,8 @@ fn run_window(window: &Arc<str>, action: &WindowAction, app: &mut AppState) {
|
||||
}
|
||||
}
|
||||
|
||||
const THUMP_AUDIO_WAV: &'static [u8] = include_bytes!("../../res/380885.wav");
|
||||
|
||||
fn audio_thump(app: &mut AppState) {
|
||||
if let Some(handle) = app.audio.get_handle() {
|
||||
let wav = include_bytes!("../../res/380885.wav");
|
||||
let cursor = Cursor::new(wav);
|
||||
let source = Decoder::new_wav(cursor).unwrap();
|
||||
let _ = handle.play_raw(source.convert_samples());
|
||||
}
|
||||
app.audio.play(THUMP_AUDIO_WAV);
|
||||
}
|
||||
@@ -29,6 +29,9 @@ pub enum LabelContent {
|
||||
format: Arc<str>,
|
||||
timezone: Option<Arc<str>>,
|
||||
},
|
||||
Timer {
|
||||
format: Arc<str>,
|
||||
},
|
||||
Battery {
|
||||
device: usize,
|
||||
low_threshold: f32,
|
||||
@@ -49,6 +52,10 @@ pub enum LabelData {
|
||||
format: Arc<str>,
|
||||
timezone: Option<Tz>,
|
||||
},
|
||||
Timer {
|
||||
format: Arc<str>,
|
||||
start: Instant,
|
||||
},
|
||||
Exec {
|
||||
last_exec: Instant,
|
||||
interval: f32,
|
||||
@@ -85,6 +92,10 @@ pub fn modular_label_init(label: &mut ModularControl, content: &LabelContent) {
|
||||
timezone: tz,
|
||||
})
|
||||
}
|
||||
LabelContent::Timer { format } => Some(LabelData::Timer {
|
||||
format: format.clone(),
|
||||
start: Instant::now(),
|
||||
}),
|
||||
LabelContent::Exec { command, interval } => Some(LabelData::Exec {
|
||||
last_exec: Instant::now(),
|
||||
interval: *interval,
|
||||
@@ -155,6 +166,14 @@ pub(super) fn label_update(control: &mut ModularControl, _: &mut (), app: &mut A
|
||||
control.set_text(&format!("{}", &date.format(&format)));
|
||||
}
|
||||
}
|
||||
LabelData::Timer { format, start } => {
|
||||
let mut format = format.clone().to_lowercase();
|
||||
let duration = start.elapsed().as_secs();
|
||||
format = format.replace("%s", &format!("{:02}", (duration % 60)));
|
||||
format = format.replace("%m", &format!("{:02}", ((duration / 60) % 60)));
|
||||
format = format.replace("%h", &format!("{:02}", ((duration / 60) / 60)));
|
||||
control.set_text(&format);
|
||||
}
|
||||
LabelData::Exec {
|
||||
last_exec,
|
||||
interval,
|
||||
|
||||
+18
-16
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::Cursor,
|
||||
process::{Child, Command},
|
||||
str::FromStr,
|
||||
};
|
||||
@@ -18,7 +17,6 @@ use crate::{
|
||||
use glam::{vec2, vec3a, Affine2, Vec4};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rodio::{Decoder, Source};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const PIXELS_PER_UNIT: f32 = 80.;
|
||||
@@ -83,10 +81,14 @@ where
|
||||
verbs: key_events_for_macro(macro_verbs),
|
||||
});
|
||||
} else if let Some(exec_args) = LAYOUT.exec_commands.get(key) {
|
||||
if exec_args.is_empty() {
|
||||
log::error!("Keyboard: EXEC args empty for {}", key);
|
||||
continue;
|
||||
}
|
||||
maybe_state = Some(KeyButtonData::Exec {
|
||||
program: exec_args
|
||||
.first()
|
||||
.expect("Keyboard: Invalid EXEC args")
|
||||
.unwrap() // safe because we checked is_empty
|
||||
.clone(),
|
||||
args: exec_args.iter().skip(1).cloned().collect(),
|
||||
});
|
||||
@@ -227,18 +229,12 @@ struct KeyboardData {
|
||||
processes: Vec<Child>,
|
||||
}
|
||||
|
||||
const KEY_AUDIO_WAV: &'static [u8] = include_bytes!("../res/421581.wav");
|
||||
|
||||
impl KeyboardData {
|
||||
fn key_click(&mut self, app: &mut AppState) {
|
||||
if !app.session.config.keyboard_sound_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(handle) = app.audio.get_handle() {
|
||||
// https://freesound.org/people/UberBosser/sounds/421581/
|
||||
let wav = include_bytes!("../res/421581.wav");
|
||||
let cursor = Cursor::new(wav);
|
||||
let source = Decoder::new_wav(cursor).unwrap();
|
||||
let _ = handle.play_raw(source.convert_samples());
|
||||
if app.session.config.keyboard_sound_enabled {
|
||||
app.audio.play(KEY_AUDIO_WAV);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +249,7 @@ enum KeyButtonData {
|
||||
static LAYOUT: Lazy<Layout> = Lazy::new(Layout::load_from_disk);
|
||||
|
||||
static MACRO_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^([A-Za-z0-1_-]+)(?: +(UP|DOWN))?$").unwrap());
|
||||
Lazy::new(|| Regex::new(r"^([A-Za-z0-1_-]+)(?: +(UP|DOWN))?$").unwrap()); // want panic
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Layout {
|
||||
@@ -314,11 +310,17 @@ impl Layout {
|
||||
key = &key[3..];
|
||||
}
|
||||
if key.contains('_') {
|
||||
key = key.split('_').next().unwrap();
|
||||
key = key.split('_').next().unwrap_or_else(|| {
|
||||
log::error!(
|
||||
"keyboard.yaml: Key '{}' must not start or end with '_'!",
|
||||
key
|
||||
);
|
||||
"???"
|
||||
});
|
||||
}
|
||||
vec![format!(
|
||||
"{}{}",
|
||||
key.chars().next().unwrap().to_uppercase(),
|
||||
key.chars().next().unwrap().to_uppercase(), // safe because we checked is_empty
|
||||
&key[1..].to_lowercase()
|
||||
)]
|
||||
}
|
||||
|
||||
+35
-13
@@ -293,19 +293,18 @@ impl ScreenRenderer {
|
||||
output: &WlxOutput,
|
||||
token: Option<&str>,
|
||||
session: &AppSession,
|
||||
) -> Option<(
|
||||
) -> anyhow::Result<(
|
||||
ScreenRenderer,
|
||||
Option<String>, /* pipewire restore token */
|
||||
)> {
|
||||
let name = output.name.clone();
|
||||
let embed_mouse = !session.config.double_cursor_fix;
|
||||
let select_screen_result =
|
||||
futures::executor::block_on(pipewire_select_screen(token, embed_mouse, true, true))
|
||||
.ok()?;
|
||||
futures::executor::block_on(pipewire_select_screen(token, embed_mouse, true, true))?;
|
||||
|
||||
let capture = PipewireCapture::new(name, select_screen_result.node_id, 60);
|
||||
|
||||
Some((
|
||||
Ok((
|
||||
ScreenRenderer {
|
||||
name: output.name.clone(),
|
||||
capture: Box::new(capture),
|
||||
@@ -551,17 +550,26 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((renderer, restore_token)) = ScreenRenderer::new_pw(output, token, session) {
|
||||
capture = Some(renderer);
|
||||
match ScreenRenderer::new_pw(output, token, session) {
|
||||
Ok((renderer, restore_token)) => {
|
||||
capture = Some(renderer);
|
||||
|
||||
if let Some(token) = restore_token {
|
||||
if pw_token_store
|
||||
.insert(String::from(display_name), token.clone())
|
||||
.is_none()
|
||||
{
|
||||
log::info!("Adding Pipewire token {}", token);
|
||||
if let Some(token) = restore_token {
|
||||
if pw_token_store
|
||||
.insert(String::from(display_name), token.clone())
|
||||
.is_none()
|
||||
{
|
||||
log::info!("Adding Pipewire token {}", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"{}: Failed to create Pipewire capture: {:?}",
|
||||
&output.name,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(capture) = capture {
|
||||
@@ -694,7 +702,7 @@ where
|
||||
O: Default,
|
||||
{
|
||||
let mut overlays = vec![];
|
||||
let wl = WlxClient::new().ok_or_else(|| anyhow::anyhow!("Failed to connect to Wayland"))?;
|
||||
let mut wl = WlxClient::new().ok_or_else(|| anyhow::anyhow!("Failed to connect to Wayland"))?;
|
||||
|
||||
// Load existing Pipewire tokens from file
|
||||
let mut pw_tokens: HashMap<String, String> = if let Ok(conf) = load_pw_token_config() {
|
||||
@@ -705,6 +713,20 @@ where
|
||||
|
||||
let pw_tokens_copy = pw_tokens.clone();
|
||||
|
||||
let mut origin = (i32::MAX, i32::MAX);
|
||||
for output in wl.outputs.values() {
|
||||
origin.0 = origin.0.min(output.logical_pos.0);
|
||||
origin.1 = origin.1.min(output.logical_pos.1);
|
||||
}
|
||||
|
||||
log::info!("Desktop origin: {:?}", origin);
|
||||
|
||||
// adjust all outputs so that the top-left corner is at (0, 0)
|
||||
for output in wl.outputs.values_mut() {
|
||||
output.logical_pos.0 -= origin.0;
|
||||
output.logical_pos.1 -= origin.1;
|
||||
}
|
||||
|
||||
for id in wl.outputs.keys() {
|
||||
if let Some(overlay) = try_create_screen(&wl, *id, &mut pw_tokens, session) {
|
||||
overlays.push(overlay);
|
||||
|
||||
+2
-11
@@ -1,12 +1,9 @@
|
||||
use std::{
|
||||
io::Cursor,
|
||||
ops::Add,
|
||||
sync::{atomic::AtomicUsize, Arc},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use rodio::{Decoder, Source};
|
||||
|
||||
use glam::vec3a;
|
||||
|
||||
use crate::{
|
||||
@@ -21,6 +18,7 @@ use crate::{
|
||||
const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
const TOAST_AUDIO_WAV: &'static [u8] = include_bytes!("../res/557297.wav");
|
||||
|
||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
@@ -79,12 +77,7 @@ impl Toast {
|
||||
.enqueue_at(TaskType::DropOverlay(selector), destroy_at);
|
||||
|
||||
if has_sound {
|
||||
if let Some(handle) = app.audio.get_handle() {
|
||||
let wav = include_bytes!("../res/557297.wav");
|
||||
let cursor = Cursor::new(wav);
|
||||
let source = Decoder::new_wav(cursor).unwrap();
|
||||
let _ = handle.play_raw(source.convert_samples());
|
||||
}
|
||||
app.audio.play(TOAST_AUDIO_WAV);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +129,6 @@ fn new_toast(
|
||||
canvas.panel(0., 0., size.0, size.1);
|
||||
|
||||
if toast.body.len() > 0 {
|
||||
log::info!("{}: {}", title, toast.body);
|
||||
canvas.label(PADDING.0, 54., og_width, size.1 - 54., toast.body);
|
||||
|
||||
canvas.fg_color = color_parse("#101010").unwrap(); // want panic
|
||||
@@ -144,7 +136,6 @@ fn new_toast(
|
||||
canvas.panel(0., 0., size.0, 30.);
|
||||
canvas.label_centered(PADDING.0, 16., og_width, FONT_SIZE as f32 + 2., title);
|
||||
} else {
|
||||
log::info!("Toast: {}", title);
|
||||
canvas.label_centered(PADDING.0, 0., og_width, size.1, title);
|
||||
}
|
||||
|
||||
|
||||
+12
-32
@@ -22,47 +22,27 @@ row_size: 23
|
||||
|
||||
# Specifies the size of each key. The sum of any given row must equal RowSize
|
||||
key_sizes:
|
||||
- [1.5,0.5, 1, 1, 1, 1,0.5,1, 1, 1, 1,0.5,1, 1, 1, 1, 0.5, 1, 1, 1, 4.5]
|
||||
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
|
||||
- [1.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.5, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
|
||||
- [1.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.25, 4, 1, 1, 1, 1]
|
||||
- [2.25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.75, 1.5, 1, 1.5, 1, 1, 1, 1]
|
||||
- [1.25, 1.25, 1.25, 6.25, 1.25, 1.25, 1.25, 1.25, 0.5, 1, 1, 1, 0.5, 2, 1, 1]
|
||||
- [1.5,0.5, 1, 1, 1, 1,0.5,1, 1, 1, 1,0.5,1, 1, 1, 1, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
|
||||
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
|
||||
- [1.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.5, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
|
||||
- [1.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.25, 4, 1, 1, 1, 1]
|
||||
- [2.25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.75, 1.5, 1, 1.5, 1, 1, 1, 1]
|
||||
- [1.25, 1.25, 1.25, 6.25, 1.25, 1.25, 1.25, 1.25, 0.5, 1, 1, 1, 0.5, 2, 1, 1]
|
||||
|
||||
# The main (blue) layout of the keyboard.
|
||||
# Accepted are:
|
||||
# - virtual keys, for a full list go to https://github.com/galister/X11Overlay/blob/master/Types/VirtualKey.cs
|
||||
# - ExecCommands (defined below)
|
||||
# - Macros (defined below)
|
||||
# - virtual keys. For a full list, look at enum VirtualKey in https://github.com/galister/wlx-overlay-s/blob/main/src/hid.rs
|
||||
# - exec_commands (defined below)
|
||||
# - macros (defined below)
|
||||
# - ~ (null) will leave an empty space with the corresponding size from key_sizes
|
||||
main_layout:
|
||||
- ["Escape", ~, "F1", "F2", "F3", "F4", ~, "F5", "F6", "F7", "F8", ~, "F9", "F10", "F11", "F12", ~, "Print", "Scroll", "Pause", ~]
|
||||
- ["Escape", ~, "F1", "F2", "F3", "F4", ~, "F5", "F6", "F7", "F8", ~, "F9", "F10", "F11", "F12", ~, "Print", "Scroll", "Pause", ~, "COPY", "PASTE", ~, "KILL"]
|
||||
- ["Oem3", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", "N0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"]
|
||||
- ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"]
|
||||
- ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~]
|
||||
- ["LShift", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"]
|
||||
- ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~]
|
||||
|
||||
# When using the purple pointer...
|
||||
# None - No special functionality when using purple pointer
|
||||
# Shift - Use same functionality as the orange pointer
|
||||
# Ctrl - Use Main layout with Ctrl modifier
|
||||
# Super - Use Main layout with Super (WinKey) modifier
|
||||
# Meta - Use Main layout with Meta (AltGr) modifier
|
||||
# Layout - Use the alt_layout defined below
|
||||
alt_layout_mode: Ctrl
|
||||
|
||||
# The alt (fn) layout of the keyboard. Only takes effect when alt_layout_mode: Layout
|
||||
# Accepted values are the same as for MainLayout.
|
||||
# The default layout here is a dummy. Change it to fit your liking.
|
||||
alt_layout:
|
||||
- ["Escape", ~, "F1", "F2", "F3", "F4", ~, "F5", "F6", "F7", "F8", ~, "F9", "F10", "F11", "F12", ~, "Print", "Scroll", "Pause", ~, "COPY", "PASTE"]
|
||||
- ["Oem3", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"]
|
||||
- ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"]
|
||||
- ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~]
|
||||
- ["LShift", "Oem102", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"]
|
||||
- ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~]
|
||||
|
||||
# Shell commands to be used in a layout.
|
||||
# Value is an array of string arguments.
|
||||
exec_commands:
|
||||
@@ -70,7 +50,7 @@ exec_commands:
|
||||
|
||||
# Series of keypresses to be used in a layout.
|
||||
# Format: keyName [DOWN|UP]
|
||||
# keyName must be a valid key from `xmodmap -pke`
|
||||
# keyName must be a valid virtual key from the VirtualKey enum (see above)
|
||||
# DOWN|UP: can be omitted for an implicit "keyName DOWN, keyName UP"
|
||||
macros:
|
||||
KILL: [ "LSuper DOWN", "LCtrl DOWN", "Escape", "LCtrl UP", "LSuper UP" ]
|
||||
@@ -78,7 +58,7 @@ macros:
|
||||
PASTE: [ "LCtrl DOWN", "V", "LCtrl UP" ]
|
||||
|
||||
# Custom labels to use.
|
||||
# Key: element of MainLayout / AltLayout
|
||||
# Key: element of main_layout
|
||||
# Value: Array of strings. 0th element is the upper row, 1st element is lower row.
|
||||
# For empty labels, use [] (do not use ~)
|
||||
labels:
|
||||
|
||||
+19
-4
@@ -1,8 +1,8 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::{io::Cursor, path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::bail;
|
||||
use glam::Vec3;
|
||||
use rodio::{OutputStream, OutputStreamHandle};
|
||||
use rodio::{Decoder, OutputStream, OutputStreamHandle, Source};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use vulkano::format::Format;
|
||||
@@ -126,18 +126,33 @@ impl AudioOutput {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_handle(&mut self) -> Option<&OutputStreamHandle> {
|
||||
fn get_handle(&mut self) -> Option<&OutputStreamHandle> {
|
||||
if self.audio_stream.is_none() && self.first_try {
|
||||
self.first_try = false;
|
||||
if let Ok((stream, handle)) = OutputStream::try_default() {
|
||||
self.audio_stream = Some((stream, handle));
|
||||
} else {
|
||||
log::error!("Failed to open audio stream");
|
||||
log::error!("Failed to open audio stream. Audio will not work.");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.audio_stream.as_ref().map(|(_, h)| h)
|
||||
}
|
||||
|
||||
pub fn play(&mut self, wav_bytes: &'static [u8]) {
|
||||
let Some(handle) = self.get_handle() else {
|
||||
return;
|
||||
};
|
||||
let cursor = Cursor::new(wav_bytes);
|
||||
let source = match Decoder::new_wav(cursor) {
|
||||
Ok(source) => source,
|
||||
Err(e) => {
|
||||
log::error!("Failed to play sound: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let _ = handle.play_raw(source.convert_samples());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScreenMeta {
|
||||
|
||||
Reference in new issue
Block a user