Compare commits

..
Author SHA1 Message Date
galister db777d7937 XSHM & PipeWire CPU capture fixes 2026-10-23 21:11:33 +09:00
galister f94c0c4ace desktop_finder rework (#697)
* desktop_finder rework

* fix args for wayvrctl process-launch
2026-10-23 19:29:25 +09:00
Aleksander 5f4c97794c wayvr: prevent alpha from going NaN again 2026-10-22 18:04:40 +02:00
Aleksander d9288a877c dash-frontend: refactoring: deduplicate dropdown code 2026-10-21 19:57:14 +02:00
13 changed files with 304 additions and 214 deletions

No files matched your search

Generated
+1 -2
View File
@@ -4773,8 +4773,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rxscreen"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbab13f83503a8272b7150d487494992cf6c59c299dcaf5d8ffac75bad9fc96"
source = "git+https://github.com/galister/rxscreen.git?rev=7197e1de6622b65859e6f2060b5750185e0dad2a#7197e1de6622b65859e6f2060b5750185e0dad2a"
dependencies = [
"libc",
]
+57 -66
View File
@@ -1,16 +1,14 @@
use std::rc::Rc;
use crate::tab::settings::{self, SettingType, Task, horiz_cell, mount_requires_restart};
use crate::{
tab::settings::{self, SettingType, Task, horiz_cell, mount_requires_restart},
util::wgui_simple,
};
use wgui::{
components::{
button::{ButtonClickEvent, ComponentButton},
checkbox::ComponentCheckbox,
slider::ComponentSlider,
},
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
task::Tasks,
widget::label::WidgetLabel,
windowing::context_menu,
};
use wlx_common::{DesktopBackend, XrBackend, config::GeneralConfig, dash_interface::InterfaceFeats};
@@ -297,7 +295,6 @@ where
mp.idx += 1;
let mut params = TemplateParams::new();
params.insert("id", &id);
match setting.get_translation() {
Ok(translation) => params.insert("translation", translation),
@@ -310,69 +307,63 @@ where
let id_cell = horiz_cell(mp.layout, parent)?;
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, id_cell, params)?;
let setting_str = setting.as_ref();
let title = setting.get_enum_title(mp.config);
let feats = mp.feats;
let cells = EnumType::VARIANTS
.iter()
.filter_map(|item| {
if item.get_bool("Hidden").unwrap_or(false) {
return None;
}
if item
.get_str("Backend")
.is_some_and(|x| XrBackend::try_from(x).unwrap() != feats.xr_backend)
{
return None;
}
if item
.get_str("Desktop")
.is_some_and(|x| DesktopBackend::try_from(x).unwrap() != feats.desktop_backend)
{
return None;
}
let value = item.as_ref();
let title = SettingType::get_enum_title_inner(*item);
let tooltip = SettingType::get_enum_tooltip_inner(*item);
let text = &title.text;
let translated = if title.translated { "1" } else { "0" };
let action = format!("{setting_str};{id};{value};{text};{translated}");
Some(context_menu::Cell::Button(context_menu::Button {
action_name: Some(action.into()),
title,
tooltip,
..Default::default()
}))
})
.collect();
let tasks = mp.tasks.clone();
wgui_simple::mount_dropdown(wgui_simple::MountDropdownParams {
layout: mp.layout,
parser_state: mp.parser_state,
doc_params: mp.doc_params,
parent: id_cell,
id,
params,
current_text: title,
cells,
on_open: Box::new(move |pos, cells| tasks.push(Task::OpenContextMenu(pos, cells))),
})?;
if setting.requires_restart() {
mount_requires_restart(mp.layout, id_cell)?;
}
let setting_str = setting.as_ref();
let title = setting.get_enum_title(mp.config);
{
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), title);
}
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let feats = mp.feats;
let tasks = mp.tasks.clone();
move |_common, e: ButtonClickEvent| {
tasks.push(Task::OpenContextMenu(
e.mouse_pos_absolute.into(),
EnumType::VARIANTS
.iter()
.filter_map(|item| {
if item.get_bool("Hidden").unwrap_or(false) {
return None;
}
if item
.get_str("Backend")
.is_some_and(|x| XrBackend::try_from(x).unwrap() != feats.xr_backend)
{
return None;
}
if item
.get_str("Desktop")
.is_some_and(|x| DesktopBackend::try_from(x).unwrap() != feats.desktop_backend)
{
return None;
}
let value = item.as_ref();
let title = SettingType::get_enum_title_inner(*item);
let tooltip = SettingType::get_enum_tooltip_inner(*item);
let text = &title.text;
let translated = if title.translated { "1" } else { "0" };
Some(context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()),
title,
tooltip,
..Default::default()
}))
})
.collect(),
));
Ok(())
}
}));
Ok(())
}
+65 -85
View File
@@ -1,7 +1,6 @@
use std::rc::Rc;
use wgui::{
components::button::{ButtonClickEvent, ComponentButton},
globals::WguiGlobals,
i18n::Translation,
layout::WidgetID,
@@ -9,7 +8,7 @@ use wgui::{
parser::{Fetchable, TemplateParams},
task::Tasks,
widget::label::WidgetLabel,
windowing::context_menu::{self},
windowing::context_menu,
};
use wlx_common::{async_executor::AsyncExecutor, config::GeneralConfig, dash_interface::ConfigChangeKind};
@@ -21,6 +20,7 @@ use crate::{
},
util::{
popup_manager::PopupHolder,
wgui_simple,
whisper::{
WHISPER_MODELS, WhisperModel, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name,
whisper_model_path,
@@ -319,47 +319,35 @@ fn whisper_gpu_select_dropdown(
let parent = horiz_cell(mp.layout, parent)?;
let mut params = TemplateParams::new();
params.insert("id", &id);
params.insert("translation", "APP_SETTINGS.WHISPER_GPU_SELECT");
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
let current_text = Translation::from_raw_text_string(mp.config.whisper_gpu.to_string());
{
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(
&mut mp.layout.state.globals.get(),
Translation::from_raw_text_string(mp.config.whisper_gpu.to_string()),
);
}
let cells = vulkan_devices
.iter()
.enumerate()
.filter(|(_, name)| !name.contains("llvmpipe"))
.map(|(idx, name)| {
context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_gpu;{};{}", id, idx).into()),
title: Translation::from_raw_text_string(name.to_string()),
..Default::default()
})
})
.collect();
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let parent_tasks = mp.tasks.clone();
move |_common, e: ButtonClickEvent| {
let mut cells = Vec::new();
for (idx, name) in vulkan_devices.iter().enumerate() {
if name.contains("llvmpipe") {
continue;
}
cells.push(context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_gpu;{};{}", id, idx).into()),
title: Translation::from_raw_text_string(name.to_string()),
..Default::default()
}));
}
parent_tasks.push(ParentTask::OpenContextMenu(e.mouse_pos_absolute.into(), cells));
Ok(())
}
}));
Ok(())
let tasks = mp.tasks.clone();
wgui_simple::mount_dropdown(wgui_simple::MountDropdownParams {
layout: mp.layout,
parser_state: mp.parser_state,
doc_params: mp.doc_params,
parent,
id,
params,
current_text,
cells,
on_open: Box::new(move |pos, cells| tasks.push(ParentTask::OpenContextMenu(pos, cells))),
})
}
fn whisper_models_dropdown(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
@@ -382,57 +370,49 @@ fn whisper_models_dropdown(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Re
});
let mut params = TemplateParams::new();
params.insert("id", &id);
params.insert("translation", "APP_SETTINGS.WHISPER_MODEL");
params.insert("tooltip", "APP_SETTINGS.WHISPER_MODEL_HELP");
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
{
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), current_translation);
}
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let parent_tasks = mp.tasks.clone();
move |_common, e: ButtonClickEvent| {
let mut cells = WHISPER_MODELS
.iter()
.enumerate()
.map(|(idx, item)| {
context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_model;{id};{}", item.file_name).into()),
title: Translation::from_raw_text(item.display_name),
tooltip: None,
attribs: vec![],
sprite_src_builtin: if downloaded_models.contains(&idx) {
Some("@/dashboard/check.svg".into())
} else {
None
},
})
})
.collect::<Vec<_>>();
cells.insert(
0,
let mut cells = WHISPER_MODELS
.iter()
.enumerate()
.map(|(idx, item)| {
if downloaded_models.contains(&idx) {
context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_model;{id};").into()),
title: Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"),
tooltip: None,
attribs: vec![],
action_name: Some(format!(";whisper_model;{id};{}", item.file_name).into()),
title: Translation::from_raw_text(item.display_name),
sprite_src_builtin: Some("@/dashboard/check.svg".into()),
..Default::default()
}),
);
})
} else {
context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_model;{id};{}", item.file_name).into()),
title: Translation::from_raw_text(item.display_name),
..Default::default()
})
}
})
.collect::<Vec<_>>();
parent_tasks.push(ParentTask::OpenContextMenu(e.mouse_pos_absolute.into(), cells));
Ok(())
}
}));
cells.insert(
0,
context_menu::Cell::Button(context_menu::Button {
action_name: Some(format!(";whisper_model;{id};").into()),
title: Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"),
..Default::default()
}),
);
Ok(())
let tasks = mp.tasks.clone();
wgui_simple::mount_dropdown(wgui_simple::MountDropdownParams {
layout: mp.layout,
parser_state: mp.parser_state,
doc_params: mp.doc_params,
parent,
id,
params,
current_text: current_translation,
cells,
on_open: Box::new(move |pos, cells| tasks.push(ParentTask::OpenContextMenu(pos, cells))),
})
}
+43 -2
View File
@@ -1,12 +1,16 @@
use glam::{Mat4, Vec2};
use std::rc::Rc;
use wgui::{
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
color::WguiColorName,
components::{self, button::ButtonClickCallback},
components::{
self,
button::{ButtonClickCallback, ButtonClickEvent, ComponentButton},
},
i18n::Translation,
layout::{Layout, LayoutTask, WidgetID},
parser::{Fetchable, ParseDocumentParams},
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
renderer_vk::{
text::{FontWeight, TextStyle, custom_glyph::CustomGlyphData},
util::centered_matrix,
@@ -17,6 +21,7 @@ use wgui::{
label::{WidgetLabel, WidgetLabelParams},
sprite::{WidgetSprite, WidgetSpriteParams},
},
windowing::context_menu,
};
pub struct CreateButtonParams<'a> {
@@ -166,3 +171,39 @@ pub fn create_loading(par: CreateLoadingParams) -> anyhow::Result<WidgetID> {
Ok(id_root)
}
pub struct MountDropdownParams<'a> {
pub layout: &'a mut Layout,
pub parser_state: &'a mut ParserState,
pub doc_params: &'a ParseDocumentParams<'a>,
pub parent: WidgetID,
pub id: String,
pub params: TemplateParams,
pub current_text: Translation,
pub cells: Vec<context_menu::Cell>,
pub on_open: Box<dyn Fn(context_menu::Position, Vec<context_menu::Cell>)>,
}
pub fn mount_dropdown(par: MountDropdownParams) -> anyhow::Result<()> {
let mut params = par.params;
params.insert("id", &par.id);
par
.parser_state
.instantiate_template(par.doc_params, "DropdownButton", par.layout, par.parent, params)?;
{
let mut label = par
.parser_state
.fetch_widget_as::<WidgetLabel>(&par.layout.state, &format!("{}_value", par.id))?;
label.set_text_simple(&mut par.layout.state.globals.get(), par.current_text);
}
let btn = par.parser_state.fetch_component_as::<ComponentButton>(&par.id)?;
btn.on_click(Rc::new(move |_common, e: ButtonClickEvent| {
(par.on_open)(e.mouse_pos_absolute.into(), par.cells.clone());
Ok(())
}));
Ok(())
}
+16 -31
View File
@@ -3,17 +3,13 @@ use std::rc::Rc;
use strum::EnumProperty;
use wgui::{
assets::AssetPathRef,
components::{
button::{ButtonClickEvent, ComponentButton},
slider::ComponentSlider,
},
components::{button::ComponentButton, slider::ComponentSlider},
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, WidgetID},
log::LogErr,
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
task::Tasks,
widget::label::WidgetLabel,
windowing::context_menu::{self, TickResult},
};
use wlx_common::{
@@ -1012,7 +1008,7 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
.iter()
.map(|item| {
context_menu::Cell::Button(context_menu::Button {
action_name: Some(item.action_str(&action, side)),
action_name: Some(item.action_str(&action, side).into()),
title: item.translation(),
..Default::default()
})
@@ -1023,7 +1019,7 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
cells.insert(
0,
context_menu::Cell::Button(context_menu::Button {
action_name: Some(action_str),
action_name: Some(action_str.into()),
title: Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"),
..Default::default()
}),
@@ -1036,35 +1032,24 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
fn create_dropdown_with_cells(
mp: &mut MacroParams,
parent: WidgetID,
mut params: TemplateParams,
params: TemplateParams,
current_text: Translation,
cells: Vec<context_menu::Cell>,
) -> anyhow::Result<()> {
let id = mp.idx.to_string();
mp.idx += 1;
params.insert("id", &id);
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
{
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), current_text);
}
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let tasks = mp.tasks.clone();
let cells = cells.clone();
move |_common, e: ButtonClickEvent| {
tasks.push(Task::OpenContextMenu(e.mouse_pos_absolute.into(), cells.clone()));
Ok(())
}
}));
Ok(())
let tasks = mp.tasks.clone();
wgui_simple::mount_dropdown(wgui_simple::MountDropdownParams {
layout: mp.layout,
parser_state: mp.parser_state,
doc_params: mp.doc_params,
parent,
id,
params,
current_text,
cells,
on_open: Box::new(move |pos, cells| tasks.push(Task::OpenContextMenu(pos, cells))),
})
}
fn apply_subpath(
+63 -19
View File
@@ -31,6 +31,9 @@ use crate::{
const CURSOR_SIZE: f32 = 16. / 1440.;
// synchronize queue shared by the WlxCapture worker threads
static CAPTURE_QUEUE_MUTEX: Mutex<()> = Mutex::new(());
struct BufPass {
pass: WGfxPass<Vert2Uv>,
buf_vert: Arc<Buffer<Vert2Uv>>,
@@ -664,6 +667,17 @@ fn upload_image(
height: u32,
format: Format,
data: &[u8],
) -> Option<Arc<Image>> {
upload_image_with_row_length(me, width, height, format, data, 0)
}
fn upload_image_with_row_length(
me: &WlxCaptureIn,
width: u32,
height: u32,
format: Format,
data: &[u8],
buffer_row_length: u32,
) -> Option<Arc<Image>> {
let cmd_result = if me.use_capture_queue {
me.gfx
@@ -679,15 +693,23 @@ fn upload_image(
return None;
}
};
let image = match cmd_xfer.upload_image(width, height, format, data) {
Ok(x) => x,
Err(e) => {
log::error!("{}: Could not create vkImage: {:?}", me.name, e);
return None;
}
};
let image =
match cmd_xfer.upload_image_with_row_length(width, height, format, data, buffer_row_length)
{
Ok(x) => x,
Err(e) => {
log::error!("{}: Could not create vkImage: {:?}", me.name, e);
return None;
}
};
if let Err(e) = cmd_xfer.build_and_execute_now() {
let result = if me.use_capture_queue {
let _guard = CAPTURE_QUEUE_MUTEX.lock().unwrap(); // mutex poison only
cmd_xfer.build_and_execute_now()
} else {
cmd_xfer.build_and_execute_now()
};
if let Err(e) = result {
log::error!("{}: Could not execute upload: {:?}", me.name, e);
return None;
}
@@ -757,8 +779,22 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
}
};
let data = unsafe { std::slice::from_raw_parts(frame.ptr as *const u8, frame.size) };
let image = upload_image(me, frame.format.width, frame.format.height, format, data)?;
// SAFETY: the MemPtr params & allocation and must be valid and must outlive the callback
let pixels = unsafe {
std::slice::from_raw_parts(
(frame.ptr as *const u8).add(frame.offset),
frame.size - frame.offset,
)
};
let buffer_row_length = (frame.stride / 4) as u32;
let image = upload_image_with_row_length(
me,
frame.format.width,
frame.format.height,
format,
pixels,
buffer_row_length,
)?;
Some(WlxCaptureOut {
image: me.gfx.create_image_view(image).ok()?,
@@ -811,6 +847,8 @@ pub(super) enum MainThreadFrame {
Cpu {
format: FrameFormat,
data: Vec<u8>,
offset: usize,
stride: usize,
mouse: Option<MouseMeta>,
},
Dmabuf {
@@ -870,12 +908,16 @@ where
MainThreadFrame::Cpu {
format,
data,
offset,
stride,
mouse,
} => {
let frame = wlx_frame::MemPtrFrame {
format,
ptr: data.as_ptr() as usize,
size: data.len(),
offset,
stride,
mouse,
};
receive_callback(
@@ -910,22 +952,24 @@ fn receive_callback_dummy(_: &DmaExporterProxy, frame: WlxFrame) -> Option<MainT
Some(MainThreadFrame::Cpu {
format: frame.format,
data: map.as_slice().to_vec(),
offset: frame.plane.offset as _,
stride: (frame.format.width as usize).checked_mul(4)?,
mouse: frame.mouse,
})
}
WlxFrame::MemPtr(frame) => {
let data = if frame.size == 0 {
Vec::new()
} else {
if frame.ptr == 0 {
log::error!("Main-thread CPU capture received a null MemPtr frame");
return None;
}
unsafe { std::slice::from_raw_parts(frame.ptr as *const u8, frame.size) }.to_vec()
// SAFETY: Allocation must be valid & must outlive the callback
let pixels = unsafe {
std::slice::from_raw_parts(
(frame.ptr as *const u8).add(frame.offset),
frame.size - frame.offset,
)
};
Some(MainThreadFrame::Cpu {
format: frame.format,
data,
data: pixels.to_vec(),
offset: frame.offset,
stride: frame.stride,
mouse: frame.mouse,
})
}
+6 -2
View File
@@ -320,8 +320,12 @@ impl OverlayWindowConfig {
return;
}
let to_hmd = (state.transform.translation - app.input_state.hmd.translation).normalize();
let watch_normal = state.transform.transform_vector3a(Vec3A::NEG_Z).normalize();
let to_hmd =
(state.transform.translation - app.input_state.hmd.translation).normalize_or_zero();
let watch_normal = state
.transform
.transform_vector3a(Vec3A::NEG_Z)
.normalize_or_zero();
let dot = to_hmd.dot(watch_normal);
let range = (app.session.config.watch_view_angle_max
+18 -3
View File
@@ -139,6 +139,17 @@ impl CommandBufferBuilder<CmdBufGfx> {
impl CommandBufferBuilder<CmdBufXfer> {
pub fn upload_image(&mut self, width: u32, height: u32, format: Format, data: &[u8]) -> anyhow::Result<Arc<Image>> {
self.upload_image_with_row_length(width, height, format, data, 0)
}
pub fn upload_image_with_row_length(
&mut self,
width: u32,
height: u32,
format: Format,
data: &[u8],
buffer_row_length: u32,
) -> anyhow::Result<Arc<Image>> {
let image = self.graphics.create_image(ImageCreateInfo::new_2d(
width,
height,
@@ -149,9 +160,13 @@ impl CommandBufferBuilder<CmdBufXfer> {
.graphics
.empty_buffer::<u8>(BufferUsage::TRANSFER_SRC, data.len() as u64)?;
staging.write()?.copy_from_slice(data);
self
.raw
.copy_buffer_to_image(&staging.raw, &image.raw, [0, 0, 0], [width, height, 1]);
self.raw.copy_buffer_to_image_with_row_length(
&staging.raw,
&image.raw,
[0, 0, 0],
[width, height, 1],
buffer_row_length,
);
self.keepalive.push(Box::new(staging));
self.keepalive.push(Box::new(image.clone()));
Ok(image)
+12 -1
View File
@@ -2494,11 +2494,22 @@ impl RawCommandBuffer {
image: &Arc<RawImage>,
offset: [u32; 3],
extent: [u32; 3],
) {
self.copy_buffer_to_image_with_row_length(buffer, image, offset, extent, 0);
}
pub(super) fn copy_buffer_to_image_with_row_length(
&mut self,
buffer: &RawBuffer,
image: &Arc<RawImage>,
offset: [u32; 3],
extent: [u32; 3],
buffer_row_length: u32,
) {
self.transition_image(image, ImageLayout::TransferDst);
let regions = [vk::BufferImageCopy::default()
.buffer_offset(0)
.buffer_row_length(0)
.buffer_row_length(buffer_row_length)
.buffer_image_height(0)
.image_subresource(vk::ImageSubresourceLayers {
aspect_mask: vk::ImageAspectFlags::COLOR,
+3 -1
View File
@@ -31,7 +31,9 @@ wayland-protocols = {
drm-fourcc = "2.2.0"
rxscreen = {
version = "0.1.7",
git = "https://github.com/galister/rxscreen.git",
rev = "7197e1de6622b65859e6f2060b5750185e0dad2a",
# version = "0.1.7",
features = [
"mouse",
"shm",
+2
View File
@@ -128,6 +128,8 @@ pub struct MemPtrFrame {
pub format: FrameFormat,
pub ptr: usize,
pub size: usize,
pub offset: usize,
pub stride: usize,
pub mouse: Option<MouseMeta>,
}
+16 -2
View File
@@ -376,10 +376,24 @@ where
}
}
DataType::MemPtr => {
let raw = datas[0].as_raw();
let chunk = datas[0].chunk();
let offset = chunk.offset() as usize;
let chunk_size = chunk.size() as usize;
let Some(size) = offset.checked_add(chunk_size) else {
log::error!("{name}: PipeWire chunk range overflow");
return;
};
if size > raw.maxsize as usize {
log::error!("{name}: PipeWire chunk extends beyond allocation");
return;
}
let memptr = MemPtrFrame {
format: *format,
ptr: datas[0].as_raw().data as _,
size: datas[0].chunk().size() as _,
ptr: raw.data as _,
size,
offset,
stride: chunk.stride() as _,
mouse: mouse_meta,
};
+2
View File
@@ -109,6 +109,8 @@ where
transform: Transform::Normal,
},
ptr: unsafe { image.as_ptr() as _ },
offset: 0,
stride: image.bytes_per_line() as _,
size,
mouse: d.root_mouse_position().and_then(|root_pos| {
monitor.mouse_to_local(root_pos).map(|(x, y)| MouseMeta {