sorry about monster commit
This commit is contained in:
@@ -11,7 +11,7 @@ pub fn create_anchor<O>(app: &mut AppState) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let panel = GuiPanel::new_from_template(app, "gui/anchor.xml", ())?;
|
||||
let panel = GuiPanel::new_from_template(app, "gui/anchor.xml", (), None)?;
|
||||
|
||||
Ok(OverlayData {
|
||||
state: OverlayState {
|
||||
|
||||
@@ -16,7 +16,7 @@ where
|
||||
O: Default,
|
||||
{
|
||||
let state = BarState {};
|
||||
let mut panel = GuiPanel::new_from_template(app, "gui/bar.xml", state)?;
|
||||
let mut panel = GuiPanel::new_from_template(app, "gui/bar.xml", state, None)?;
|
||||
|
||||
for (id, _widget_id) in &panel.parser_state.ids {
|
||||
match id.as_ref() {
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
use std::{
|
||||
sync::{Arc, LazyLock, atomic::AtomicU64},
|
||||
sync::{atomic::AtomicU64, Arc, LazyLock},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use glam::{Affine2, Vec2, vec2};
|
||||
use glam::{vec2, Affine2, Vec2};
|
||||
use vulkano::image::view::ImageView;
|
||||
use wlx_capture::WlxCapture;
|
||||
use wlx_capture::{frame::Transform, WlxCapture};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{Haptics, PointerHit, PointerMode},
|
||||
overlay::{FrameMeta, OverlayBackend, ShouldRender},
|
||||
},
|
||||
graphics::CommandBuffers,
|
||||
graphics::{CommandBuffers, ExtentExt},
|
||||
state::AppState,
|
||||
subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT},
|
||||
};
|
||||
|
||||
use super::{
|
||||
Transform,
|
||||
capture::{ScreenPipeline, WlxCaptureIn, WlxCaptureOut, receive_callback},
|
||||
};
|
||||
use super::capture::{receive_callback, ScreenPipeline, WlxCaptureIn, WlxCaptureOut};
|
||||
|
||||
const CURSOR_SIZE: f32 = 16. / 1440.;
|
||||
|
||||
@@ -66,17 +63,17 @@ impl ScreenBackend {
|
||||
|
||||
pub(super) fn set_mouse_transform(&mut self, pos: Vec2, size: Vec2, transform: Transform) {
|
||||
self.mouse_transform = match transform {
|
||||
Transform::_90 | Transform::Flipped90 => Affine2::from_cols(
|
||||
Transform::Rotated90 | Transform::Flipped90 => Affine2::from_cols(
|
||||
vec2(0., size.y),
|
||||
vec2(-size.x, 0.),
|
||||
vec2(pos.x + size.x, pos.y),
|
||||
),
|
||||
Transform::_180 | Transform::Flipped180 => Affine2::from_cols(
|
||||
Transform::Rotated180 | Transform::Flipped180 => Affine2::from_cols(
|
||||
vec2(-size.x, 0.),
|
||||
vec2(0., -size.y),
|
||||
vec2(pos.x + size.x, pos.y + size.y),
|
||||
),
|
||||
Transform::_270 | Transform::Flipped270 => Affine2::from_cols(
|
||||
Transform::Rotated270 | Transform::Flipped270 => Affine2::from_cols(
|
||||
vec2(0., -size.y),
|
||||
vec2(size.x, 0.),
|
||||
vec2(pos.x, pos.y + size.y),
|
||||
@@ -85,16 +82,16 @@ impl ScreenBackend {
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) fn get_interaction_transform(&mut self, res: Vec2, transform: Transform) {
|
||||
pub(super) fn set_interaction_transform(&mut self, res: Vec2, transform: Transform) {
|
||||
let center = Vec2 { x: 0.5, y: 0.5 };
|
||||
self.interaction_transform = Some(match transform {
|
||||
Transform::_90 | Transform::Flipped90 => {
|
||||
Transform::Rotated90 | Transform::Flipped90 => {
|
||||
Affine2::from_cols(Vec2::NEG_Y * (res.x / res.y), Vec2::NEG_X, center)
|
||||
}
|
||||
Transform::_180 | Transform::Flipped180 => {
|
||||
Transform::Rotated180 | Transform::Flipped180 => {
|
||||
Affine2::from_cols(Vec2::NEG_X, Vec2::NEG_Y * (-res.x / res.y), center)
|
||||
}
|
||||
Transform::_270 | Transform::Flipped270 => {
|
||||
Transform::Rotated270 | Transform::Flipped270 => {
|
||||
Affine2::from_cols(Vec2::Y * (res.x / res.y), Vec2::X, center)
|
||||
}
|
||||
_ if res.y > res.x => {
|
||||
@@ -164,9 +161,14 @@ impl OverlayBackend for ScreenBackend {
|
||||
if let Some(pipeline) = self.pipeline.as_mut() {
|
||||
if self.meta.is_some_and(|old| old.extent != meta.extent) {
|
||||
pipeline.set_extent(app, [meta.extent[0] as _, meta.extent[1] as _])?;
|
||||
self.set_interaction_transform(
|
||||
meta.extent.extent_vec2(),
|
||||
frame.get_transform(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.pipeline = Some(ScreenPipeline::new(&meta, app)?);
|
||||
self.set_interaction_transform(meta.extent.extent_vec2(), frame.get_transform());
|
||||
}
|
||||
|
||||
self.meta = Some(meta);
|
||||
|
||||
@@ -6,22 +6,21 @@ use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
device::Queue,
|
||||
format::Format,
|
||||
image::{Image, sampler::Filter, view::ImageView},
|
||||
image::{sampler::Filter, view::ImageView, Image},
|
||||
pipeline::graphics::{color_blend::AttachmentBlend, input_assembly::PrimitiveTopology},
|
||||
};
|
||||
use wgui::gfx::{WGfx, pass::WGfxPass, pipeline::WGfxPipeline};
|
||||
use wgui::gfx::{pass::WGfxPass, pipeline::WGfxPipeline, WGfx};
|
||||
use wlx_capture::{
|
||||
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, Transform, WlxFrame},
|
||||
WlxCapture,
|
||||
frame::{self as wlx_frame, DrmFormat, FrameFormat, MouseMeta, WlxFrame},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::overlay::FrameMeta,
|
||||
config::GeneralConfig,
|
||||
graphics::{
|
||||
CommandBuffers, Vert2Uv,
|
||||
dmabuf::{WGfxDmabuf, fourcc_to_vk},
|
||||
upload_quad_vertices,
|
||||
dmabuf::{fourcc_to_vk, WGfxDmabuf},
|
||||
upload_quad_vertices, CommandBuffers, Vert2Uv,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
@@ -209,6 +208,10 @@ impl WlxCaptureOut {
|
||||
format: self.image.format(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) const fn get_transform(&self) -> Transform {
|
||||
self.format.transform
|
||||
}
|
||||
}
|
||||
|
||||
fn upload_image(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::{f32::consts::PI, sync::Arc};
|
||||
|
||||
use backend::ScreenBackend;
|
||||
use glam::{Quat, Vec3, vec3a};
|
||||
use wayland_client::protocol::wl_output;
|
||||
use glam::{vec3a, Quat, Vec3};
|
||||
use wl::create_screens_wayland;
|
||||
use x11::{create_screens_x11pw, create_screens_xshm};
|
||||
use wlx_capture::frame::Transform;
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{OverlayState, Positioning},
|
||||
@@ -21,41 +20,12 @@ pub mod wl;
|
||||
#[cfg(feature = "x11")]
|
||||
pub mod x11;
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Transform {
|
||||
Normal,
|
||||
_90,
|
||||
_180,
|
||||
_270,
|
||||
Flipped,
|
||||
Flipped90,
|
||||
Flipped180,
|
||||
Flipped270,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
impl From<wl_output::Transform> for Transform {
|
||||
fn from(t: wl_output::Transform) -> Self {
|
||||
match t {
|
||||
wl_output::Transform::_90 => Self::_90,
|
||||
wl_output::Transform::_180 => Self::_180,
|
||||
wl_output::Transform::_270 => Self::_270,
|
||||
wl_output::Transform::Flipped => Self::Flipped,
|
||||
wl_output::Transform::Flipped90 => Self::Flipped90,
|
||||
wl_output::Transform::Flipped180 => Self::Flipped180,
|
||||
wl_output::Transform::Flipped270 => Self::Flipped270,
|
||||
_ => Self::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_screen_state(name: Arc<str>, transform: Transform, session: &AppSession) -> OverlayState {
|
||||
let angle = if session.config.upright_screen_fix {
|
||||
match transform {
|
||||
Transform::_90 | Transform::Flipped90 => PI / 2.,
|
||||
Transform::_180 | Transform::Flipped180 => PI,
|
||||
Transform::_270 | Transform::Flipped270 => -PI / 2.,
|
||||
Transform::Rotated90 | Transform::Flipped90 => PI / 2.,
|
||||
Transform::Rotated180 | Transform::Flipped180 => PI,
|
||||
Transform::Rotated270 | Transform::Flipped270 => -PI / 2.,
|
||||
_ => 0.,
|
||||
}
|
||||
} else {
|
||||
@@ -103,12 +73,12 @@ pub fn create_screens(app: &mut AppState) -> anyhow::Result<(ScreenCreateData, O
|
||||
.ok();
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
match create_screens_x11pw(app) {
|
||||
match x11::create_screens_x11pw(app) {
|
||||
Ok(data) => return Ok((data, keymap)),
|
||||
Err(e) => log::info!("Will not use X11 PipeWire capture: {e:?}"),
|
||||
}
|
||||
|
||||
Ok((create_screens_xshm(app)?, keymap))
|
||||
Ok((x11::create_screens_xshm(app)?, keymap))
|
||||
}
|
||||
#[cfg(not(feature = "x11"))]
|
||||
anyhow::bail!("No backends left to try.")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use glam::vec2;
|
||||
use wlx_capture::{
|
||||
WlxCapture,
|
||||
wayland::{WlxClient, WlxOutput},
|
||||
wlr_dmabuf::WlrDmabufCapture,
|
||||
wlr_screencopy::WlrScreencopyCapture,
|
||||
WlxCapture,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -13,10 +13,10 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
ScreenCreateData,
|
||||
backend::ScreenBackend,
|
||||
capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
capture::{new_wlx_capture, MainThreadWlxCapture},
|
||||
pw::{load_pw_token_config, save_pw_token_config},
|
||||
ScreenCreateData,
|
||||
};
|
||||
|
||||
impl ScreenBackend {
|
||||
@@ -126,7 +126,7 @@ pub fn create_screens_wayland(wl: &mut WlxClient, app: &mut AppState) -> ScreenC
|
||||
) {
|
||||
let logical_pos = vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32);
|
||||
let logical_size = vec2(output.logical_size.0 as f32, output.logical_size.1 as f32);
|
||||
let transform = output.transform.into();
|
||||
let transform = output.transform;
|
||||
|
||||
backend.set_mouse_transform(logical_pos, logical_size, transform);
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ use std::sync::Arc;
|
||||
|
||||
use glam::vec2;
|
||||
use wlx_capture::{
|
||||
WlxCapture,
|
||||
frame::Transform,
|
||||
xshm::{XshmCapture, XshmScreen},
|
||||
WlxCapture,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -12,9 +13,9 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
ScreenCreateData, Transform,
|
||||
backend::ScreenBackend,
|
||||
capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
capture::{new_wlx_capture, MainThreadWlxCapture},
|
||||
ScreenCreateData,
|
||||
};
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use wgui::{
|
||||
i18n::Translation,
|
||||
parser::parse_color_hex,
|
||||
renderer_vk::text::TextStyle,
|
||||
taffy::{
|
||||
self,
|
||||
prelude::{auto, length, percent},
|
||||
},
|
||||
widget::{
|
||||
rectangle::{Rectangle, RectangleParams},
|
||||
text::{TextLabel, TextParams},
|
||||
util::WLength,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{OverlayBackend, OverlayState, Z_ORDER_TOAST},
|
||||
gui::panel::GuiPanel,
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
const FONT_SIZE: isize = 16;
|
||||
const PADDING: (f32, f32) = (25., 7.);
|
||||
const PIXELS_TO_METERS: f32 = 1. / 2000.;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn new_tooltip(
|
||||
text: &str,
|
||||
transform: Affine3A,
|
||||
app: &mut AppState,
|
||||
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
|
||||
let mut panel = GuiPanel::new_blank(app, ()).ok()?;
|
||||
|
||||
let globals = panel.layout.state.globals.clone();
|
||||
let mut i18n = globals.i18n();
|
||||
|
||||
let (rect, _) = panel
|
||||
.layout
|
||||
.add_child(
|
||||
panel.layout.root_widget,
|
||||
Rectangle::create(RectangleParams {
|
||||
color: parse_color_hex("#1e2030").unwrap(),
|
||||
border_color: parse_color_hex("#5e7090").unwrap(),
|
||||
border: 1.0,
|
||||
round: WLength::Units(4.0),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap(),
|
||||
taffy::Style {
|
||||
align_items: Some(taffy::AlignItems::Center),
|
||||
justify_content: Some(taffy::JustifyContent::Center),
|
||||
flex_direction: taffy::FlexDirection::Column,
|
||||
padding: length(4.0),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
let _ = panel.layout.add_child(
|
||||
rect,
|
||||
TextLabel::create(
|
||||
&mut i18n,
|
||||
TextParams {
|
||||
content: Translation::from_raw_text(text),
|
||||
style: TextStyle {
|
||||
color: parse_color_hex("#ffffff"),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
taffy::Style {
|
||||
size: taffy::Size {
|
||||
width: percent(1.0),
|
||||
height: auto(),
|
||||
},
|
||||
padding: length(8.0),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
panel.update_layout().ok()?;
|
||||
|
||||
let state = OverlayState {
|
||||
name: "tooltip".into(),
|
||||
want_visible: true,
|
||||
spawn_scale: panel.layout.content_size.x * PIXELS_TO_METERS,
|
||||
spawn_rotation: Quat::IDENTITY,
|
||||
spawn_point: Vec3A::ZERO,
|
||||
z_order: Z_ORDER_TOAST,
|
||||
positioning: crate::backend::overlay::Positioning::Static,
|
||||
..Default::default()
|
||||
};
|
||||
let backend = Box::new(panel);
|
||||
|
||||
Some((state, backend))
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
use std::{rc::Rc, time::Duration};
|
||||
use std::{collections::HashMap, rc::Rc, time::Duration};
|
||||
|
||||
use chrono::Local;
|
||||
use chrono_tz::Tz;
|
||||
use glam::Vec3A;
|
||||
use regex::Regex;
|
||||
use wgui::{
|
||||
event::{self, EventListenerKind},
|
||||
i18n::Translation,
|
||||
widget::label::WidgetLabel,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
backend::overlay::{OverlayData, OverlayState, Positioning, Z_ORDER_WATCH},
|
||||
backend::overlay::{OverlayData, OverlayID, OverlayState, Positioning, Z_ORDER_WATCH},
|
||||
gui::{panel::GuiPanel, timer::GuiTimer},
|
||||
state::AppState,
|
||||
};
|
||||
@@ -25,80 +18,39 @@ pub fn create_watch<O>(app: &mut AppState) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
let screens = app
|
||||
.screens
|
||||
.iter()
|
||||
.map(|s| s.id)
|
||||
.collect::<SmallVec<[OverlayID; 8]>>();
|
||||
|
||||
let state = WatchState {};
|
||||
let mut panel = GuiPanel::new_from_template(app, "gui/watch.xml", state)?;
|
||||
let mut panel = GuiPanel::new_from_template(
|
||||
app,
|
||||
"gui/watch.xml",
|
||||
state,
|
||||
Some(Box::new(
|
||||
move |id, widget, doc_params, layout, parser_state, listeners| {
|
||||
if &*id != "sets" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (idx, handle) in screens.iter().enumerate() {
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert("display".into(), (idx + 1).to_string().into());
|
||||
params.insert("handle".into(), handle.0.to_string().into());
|
||||
parser_state
|
||||
.process_template(doc_params, "Set", layout, listeners, widget, params)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)),
|
||||
)?;
|
||||
|
||||
panel
|
||||
.timers
|
||||
.push(GuiTimer::new(Duration::from_millis(100), 0));
|
||||
|
||||
let clock_regex = Regex::new(r"^clock([0-9])_([a-z]+)$").unwrap();
|
||||
|
||||
for (id, widget_id) in &panel.parser_state.ids {
|
||||
if let Some(cap) = clock_regex.captures(id) {
|
||||
let tz_idx: usize = cap.get(1).unwrap().as_str().parse().unwrap(); // safe due to regex
|
||||
let tz_str = (tz_idx > 0)
|
||||
.then(|| app.session.config.timezones.get(tz_idx - 1))
|
||||
.flatten();
|
||||
let role = cap.get(2).unwrap().as_str();
|
||||
|
||||
let mut label = panel
|
||||
.layout
|
||||
.state
|
||||
.widgets
|
||||
.get_as::<WidgetLabel>(*widget_id)
|
||||
.unwrap();
|
||||
|
||||
let format = match role {
|
||||
"tz" => {
|
||||
let mut i18n = panel.layout.state.globals.i18n();
|
||||
if let Some(s) =
|
||||
tz_str.and_then(|tz| tz.split('/').next_back().map(|x| x.replace('_', " ")))
|
||||
{
|
||||
label.set_text_simple(&mut i18n, Translation::from_raw_text(&s));
|
||||
} else {
|
||||
label.set_text_simple(&mut i18n, Translation::from_raw_text("Local"));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
"date" => "%x",
|
||||
"dow" => "%A",
|
||||
"time" => {
|
||||
if app.session.config.clock_12h {
|
||||
"%I:%M %p"
|
||||
} else {
|
||||
"%H:%M"
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut i18n = panel.layout.state.globals.i18n();
|
||||
label.set_text_simple(&mut i18n, Translation::from_raw_text("ERR"));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let clock = ClockState {
|
||||
timezone: tz_str.and_then(|tz| {
|
||||
tz.parse()
|
||||
.inspect_err(|e| log::warn!("Invalid timezone: {e:?}"))
|
||||
.ok()
|
||||
}),
|
||||
format: format.into(),
|
||||
};
|
||||
|
||||
panel.listeners.register(
|
||||
&mut panel.listener_handles,
|
||||
*widget_id,
|
||||
EventListenerKind::InternalStateChange,
|
||||
Box::new(move |common, data, _, _| {
|
||||
clock_on_tick(&clock, common, data);
|
||||
Ok(())
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let positioning = Positioning::FollowHand {
|
||||
hand: app.session.config.watch_hand as _,
|
||||
lerp: 1.0,
|
||||
@@ -150,22 +102,3 @@ where
|
||||
watch.state.alpha = watch.state.alpha.clamp(0., 1.);
|
||||
}
|
||||
}
|
||||
|
||||
struct ClockState {
|
||||
timezone: Option<Tz>,
|
||||
format: Rc<str>,
|
||||
}
|
||||
|
||||
fn clock_on_tick(
|
||||
clock: &ClockState,
|
||||
common: &mut event::CallbackDataCommon,
|
||||
data: &mut event::CallbackData,
|
||||
) {
|
||||
let date_time = clock.timezone.as_ref().map_or_else(
|
||||
|| format!("{}", Local::now().format(&clock.format)),
|
||||
|tz| format!("{}", Local::now().with_timezone(tz).format(&clock.format)),
|
||||
);
|
||||
|
||||
let label = data.obj.get_as_mut::<WidgetLabel>().unwrap();
|
||||
label.set_text(common, Translation::from_raw_text(&date_time));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user