dropdown for capture_method + random tweaks

This commit is contained in:
galister
2026-01-08 16:57:37 +09:00
parent 5616090fa9
commit e9230f6f9f
19 changed files with 358 additions and 82 deletions

1
Cargo.lock generated
View File

@@ -6930,6 +6930,7 @@ dependencies = [
"serde",
"serde_json",
"smol",
"strum",
"walkdir",
"wayvr-ipc",
"wgui",

View File

@@ -0,0 +1 @@
../../../wlx-overlay-s/src/assets/keyboard/down.svg

View File

@@ -0,0 +1,27 @@
<layout>
<include src="theme.xml" />
<macro name="dropdown_button"
flex_direction="row"
border="2"
color="#00000055"
border_color="#FFFFFF66"
justify_content="space_between" />
<!-- id, text, translation, tooltip -->
<template name="DropdownButton">
<label text="${text}" translation="${translation}" />
<Button id="${id}" height="32" tooltip="${tooltip}" >
<div padding_left="8" padding_right="8" min_width="200">
<label id="${id}_value" weight="bold" />
</div>
<div gap="2">
<div padding_top="4" padding_bottom="4">
<rectangle width="2" height="100%" color="#FFFFFF66" />
</div>
<sprite margin_left="-4" width="30" height="30" color="~text_color" src_builtin="dashboard/down.svg" />
</div>
</Button>
</template>
</layout>

View File

@@ -1,6 +1,7 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<include src="../t_dropdown_button.xml" />
<template name="SettingsGroupBox">
<rectangle macro="group_box" id="${id}" flex_grow="1">
@@ -27,7 +28,10 @@
</template>
<template name="DangerButton">
<Button id="${id}" color="#AA3333" height="32" width="100%" sprite_src_builtin="${icon}" translation="${translation}" tooltip="${translation}_HELP" />
<Button id="${id}" color="#AA3333" height="32" tooltip="${translation}_HELP" padding="4" gap="8" >
<sprite src_builtin="${icon}" height="24" width="24" />
<label align="left" translation="${translation}" weight="bold" min_width="200" />
</Button>
</template>
<elements>

View File

@@ -69,7 +69,16 @@
"CLEAR_SAVED_STATE_HELP": "Reset sets & overlay positions",
"CLEAR_PIPEWIRE_TOKENS_HELP": "Prompt for screen selection on next start",
"DELETE_ALL_CONFIGS_HELP": "Remove all configuration files from conf.d",
"RESTART_SOFTWARE_HELP": "Apply settings that require a restart"
"RESTART_SOFTWARE_HELP": "Apply settings that require a restart",
"CAPTURE_METHOD": "Wayland screen capture",
"CAPTURE_METHOD_HELP": "Try changing this if experiencing\nblack or glitchy screens",
"OPTION": {
"PIPEWIRE_HELP": "Fast GPU capture. Recommended",
"PW_FALLBACK_HELP": "Slow. Try in case PipeWire GPU doesn't work",
"SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway"
}
},
"APPLICATION_LAUNCHER": "Application launcher",
"APPLICATION_STARTED": "Application started",

View File

@@ -1,13 +1,18 @@
use std::{collections::HashMap, marker::PhantomData, rc::Rc};
use std::{collections::HashMap, marker::PhantomData, rc::Rc, str::FromStr};
use strum::AsRefStr;
use glam::Vec2;
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
use wgui::{
assets::AssetPath,
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
event::{CallbackDataCommon, EventAlterables},
i18n::Translation,
layout::{Layout, WidgetID},
log::LogErr,
parser::{Fetchable, ParseDocumentParams, ParserState},
task::Tasks,
widget::label::WidgetLabel,
windowing::context_menu::{self, Blueprint, ContextMenu, TickResult},
};
use wlx_common::{config::GeneralConfig, config_io::ConfigRoot};
@@ -20,6 +25,7 @@ enum Task {
UpdateBool(SettingType, bool),
UpdateFloat(SettingType, f32),
UpdateInt(SettingType, i32),
OpenContextMenu(Vec2, Vec<context_menu::Cell>),
ClearPipewireTokens,
ClearSavedState,
DeleteAllConfigs,
@@ -30,6 +36,8 @@ pub struct TabSettings<T> {
#[allow(dead_code)]
pub state: ParserState,
context_menu: ContextMenu,
tasks: Tasks<Task>,
marker: PhantomData<T>,
}
@@ -76,17 +84,56 @@ impl<T> Tab<T> for TabSettings<T> {
frontend.interface.restart(data);
return Ok(());
}
Task::OpenContextMenu(position, cells) => {
self.context_menu.open(context_menu::OpenParams {
on_custom_attribs: None,
position,
blueprint: Blueprint::Cells(cells),
});
}
}
}
// Dropdown handling
if let TickResult::Action(name) = self.context_menu.tick(&mut frontend.layout, &mut self.state)? {
if let (Some(setting), Some(id), Some(value), Some(text), Some(translated)) = {
let mut s = name.splitn(5, ';');
(s.next(), s.next(), s.next(), s.next(), s.next())
} {
let mut label = self
.state
.fetch_widget_as::<WidgetLabel>(&frontend.layout.state, &format!("{id}_value"))?;
let mut alterables = EventAlterables::default();
let mut common = CallbackDataCommon {
alterables: &mut alterables,
state: &frontend.layout.state,
};
let translation = Translation {
text: text.into(),
translated: translated == "1",
};
label.set_text(&mut common, translation);
let setting = SettingType::from_str(setting).expect("Invalid Enum string");
setting.set_enum(config, value);
changed = true;
}
}
// Notify overlays of the change
if changed {
frontend.interface.config_changed(data);
}
Ok(())
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Copy, AsRefStr)]
#[derive(Clone, Copy, AsRefStr, EnumString)]
enum SettingType {
AnimationSpeed,
RoundMultiplier,
@@ -120,6 +167,7 @@ enum SettingType {
HideUsername,
OpaqueBackground,
XwaylandByDefault,
CaptureMethod,
}
impl SettingType {
@@ -173,6 +221,40 @@ impl SettingType {
}
}
pub fn set_enum<'a>(self, config: &'a mut GeneralConfig, value: &str) {
match self {
Self::CaptureMethod => {
config.capture_method = wlx_common::config::CaptureMethod::from_str(value).expect("Invalid enum value!")
}
_ => panic!("Requested enum for non-enum SettingType"),
}
}
fn get_enum_title<'a>(self, config: &'a mut GeneralConfig) -> Translation {
match self {
Self::CaptureMethod => Self::get_enum_title_inner(config.capture_method),
_ => panic!("Requested enum for non-enum SettingType"),
}
}
fn get_enum_title_inner<E>(value: E) -> Translation
where
E: EnumProperty + AsRef<str>,
{
value
.get_str("Translation")
.map(|x| Translation::from_translation_key(x))
.or_else(|| value.get_str("Text").map(|x| Translation::from_raw_text(x)))
.unwrap_or_else(|| Translation::from_raw_text(value.as_ref()))
}
fn get_enum_tooltip_inner<E>(value: E) -> Option<Translation>
where
E: EnumProperty + AsRef<str>,
{
value.get_str("Tooltip").map(|x| Translation::from_translation_key(x))
}
/// Ok is translation, Err is raw text
fn get_translation(self) -> Result<&'static str, &'static str> {
match self {
@@ -208,6 +290,7 @@ impl SettingType {
Self::HideUsername => Ok("APP_SETTINGS.HIDE_USERNAME"),
Self::OpaqueBackground => Ok("APP_SETTINGS.OPAQUE_BACKGROUND"),
Self::XwaylandByDefault => Ok("APP_SETTINGS.XWAYLAND_BY_DEFAULT"),
Self::CaptureMethod => Ok("APP_SETTINGS.CAPTURE_METHOD"),
}
}
@@ -224,6 +307,7 @@ impl SettingType {
Self::UseSkybox => Some("APP_SETTINGS.USE_SKYBOX_HELP"),
Self::UsePassthrough => Some("APP_SETTINGS.USE_PASSTHROUGH_HELP"),
Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"),
Self::CaptureMethod => Some("APP_SETTINGS.CAPTURE_METHOD_HELP"),
_ => None,
}
}
@@ -381,6 +465,72 @@ macro_rules! slider_i32 {
};
}
macro_rules! dropdown {
($mp:expr, $root:expr, $setting:expr, $options:expr) => {
let id = $mp.idx.to_string();
$mp.idx += 1;
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
match $setting.get_translation() {
Ok(translation) => params.insert(Rc::from("translation"), translation.into()),
Err(raw_text) => params.insert(Rc::from("text"), raw_text.into()),
};
if let Some(tooltip) = $setting.get_tooltip() {
params.insert(Rc::from("tooltip"), Rc::from(tooltip));
}
$mp
.parser_state
.instantiate_template($mp.doc_params, "DropdownButton", $mp.layout, $root, params)?;
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(Box::new({
let tasks = $mp.tasks.clone();
move |_common, e| {
tasks.push(Task::OpenContextMenu(
e.mouse_pos_absolute.unwrap_or_default(),
$options
.iter()
.filter_map(|item| {
if item.get_bool("Hidden").unwrap_or(false) {
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 {
action_name: Some(format!("{setting_str};{id};{value};{text};{translated}").into()),
title,
tooltip,
attribs: vec![],
})
})
.collect(),
));
Ok(())
}
}));
};
}
macro_rules! button {
($mp:expr, $root:expr, $translation:expr, $icon:expr, $task:expr) => {
let id = $mp.idx.to_string();
@@ -474,27 +624,33 @@ impl<T> TabSettings<T> {
checkbox!(mp, c, SettingType::UprightScreenFix);
checkbox!(mp, c, SettingType::DoubleCursorFix);
checkbox!(mp, c, SettingType::ScreenRenderDown);
dropdown!(
mp,
c,
SettingType::CaptureMethod,
wlx_common::config::CaptureMethod::VARIANTS
);
let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/blocks.svg")?;
button!(
mp,
c,
"APP_SETTINGS.CLEAR_SAVED_STATE",
"dashboard/remove_circle.svg",
Task::ClearSavedState
);
button!(
mp,
c,
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
"dashboard/remove_circle.svg",
"dashboard/display.svg",
Task::ClearPipewireTokens
);
button!(
mp,
c,
"APP_SETTINGS.CLEAR_SAVED_STATE",
"dashboard/binary.svg",
Task::ClearSavedState
);
button!(
mp,
c,
"APP_SETTINGS.DELETE_ALL_CONFIGS",
"dashboard/remove_circle.svg",
"dashboard/circle.svg",
Task::DeleteAllConfigs
);
button!(
@@ -509,6 +665,7 @@ impl<T> TabSettings<T> {
tasks: mp.tasks,
state: parser_state,
marker: PhantomData,
context_menu: ContextMenu::default(),
})
}
}

View File

@@ -259,8 +259,10 @@ impl TestbedGeneric {
on_custom_attribs: Some(Rc::new(move |custom_attribs| {
log::info!("custom attribs {:?}", custom_attribs.pairs);
})),
template_name: "my_context_menu".into(),
template_params: Default::default(),
blueprint: context_menu::Blueprint::Template {
template_name: "my_context_menu".into(),
template_params: Default::default(),
},
position,
});
}

View File

@@ -1,7 +1,7 @@
<layout>
<!-- text: str -->
<template name="Cell">
<Button id="button" text="${text}" weight="bold" border="0" padding="4" color="#FFFFFF00" />
<Button id="button" text="${text}" tooltip="${tooltip}" weight="bold" border="0" padding="4" color="#FFFFFF00" />
</template>
<template name="Separator">

View File

@@ -1,10 +1,10 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
components::{Component, ComponentBase, ComponentTrait, RefreshData, checkbox::ComponentCheckbox},
components::{checkbox::ComponentCheckbox, Component, ComponentBase, ComponentTrait, RefreshData},
event::CallbackDataCommon,
layout::WidgetPair,
widget::{ConstructEssentials, div::WidgetDiv},
widget::{div::WidgetDiv, ConstructEssentials},
};
pub struct RadioValueChangeEvent {

View File

@@ -9,11 +9,11 @@ use crate::{
layout::{self, LayoutTask, LayoutTasks, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle},
widget::{
ConstructEssentials,
div::WidgetDiv,
label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
ConstructEssentials,
},
};

View File

@@ -259,11 +259,11 @@ impl ParserState {
Ok(())
}
pub(crate) fn context_menu_create_blueprint(
pub(crate) fn context_menu_parse_cells(
&mut self,
template_name: &str,
template_params: &HashMap<Rc<str>, Rc<str>>,
) -> anyhow::Result<context_menu::Blueprint> {
) -> anyhow::Result<Vec<context_menu::Cell>> {
let Some(template) = self.data.templates.get(template_name) else {
anyhow::bail!("no template named \"{template_name}\" found");
};
@@ -283,6 +283,7 @@ impl ParserState {
"" => {}
"cell" => {
let mut title: Option<Translation> = None;
let mut tooltip: Option<Translation> = None;
let mut action_name: Option<Rc<str>> = None;
let mut attribs = Vec::<AttribPair>::new();
@@ -292,6 +293,8 @@ impl ParserState {
match key {
"text" => title = Some(Translation::from_raw_text(value)),
"translation" => title = Some(Translation::from_translation_key(value)),
"tooltip" => tooltip = Some(Translation::from_translation_key(value)),
"tooltip_str" => tooltip = Some(Translation::from_raw_text(value)),
"action" => action_name = Some(value.into()),
other => {
if !other.starts_with('_') {
@@ -305,6 +308,7 @@ impl ParserState {
let title = title.context("No text/translation provided")?;
cells.push(context_menu::Cell {
title,
tooltip,
action_name,
attribs,
});
@@ -316,9 +320,7 @@ impl ParserState {
}
Ok(
context_menu::Blueprint {
cells,
}
)
}
}

View File

@@ -15,19 +15,23 @@ use crate::{
pub struct Cell {
pub title: Translation,
pub tooltip: Option<Translation>,
pub action_name: Option<Rc<str>>,
pub attribs: Vec<parser::AttribPair>,
}
pub(crate) struct Blueprint {
pub cells: Vec<Cell>,
pub enum Blueprint {
Cells(Vec<Cell>),
Template {
template_name: Rc<str>,
template_params: HashMap<Rc<str>, Rc<str>>,
},
}
pub struct OpenParams {
pub on_custom_attribs: Option<parser::OnCustomAttribsFunc>,
pub template_name: Rc<str>,
pub template_params: HashMap<Rc<str>, Rc<str>>,
pub position: Vec2,
pub blueprint: Blueprint,
}
#[derive(Clone)]
@@ -74,11 +78,17 @@ impl ContextMenu {
fn open_process(
&mut self,
params: &mut OpenParams,
params: OpenParams,
layout: &mut Layout,
parser_state: &mut ParserState,
) -> anyhow::Result<()> {
let blueprint = parser_state.context_menu_create_blueprint(&params.template_name, &params.template_params)?;
let cells = match params.blueprint {
Blueprint::Template {
template_name,
template_params,
} => parser_state.context_menu_parse_cells(&template_name, &template_params)?,
Blueprint::Cells(cells) => cells,
};
let globals = layout.state.globals.clone();
@@ -100,9 +110,13 @@ impl ContextMenu {
let id_buttons = inner_parser.get_widget_id("buttons")?;
for (idx, cell) in blueprint.cells.iter().enumerate() {
for (idx, cell) in cells.iter().enumerate() {
let mut par = HashMap::new();
par.insert(Rc::from("text"), cell.title.generate(&mut globals.i18n()));
if let Some(tooltip) = cell.tooltip.as_ref() {
par.insert(Rc::from("tooltip_str"), tooltip.generate(&mut globals.i18n()));
}
let mut data_cell = inner_parser.parse_template(&doc_params, "Cell", layout, id_buttons, par)?;
let button = data_cell.fetch_component_as::<ComponentButton>("button")?;
@@ -112,7 +126,7 @@ impl ContextMenu {
.tasks
.handle_button(&button, Task::ActionClicked(cell.action_name.clone()));
if let Some(c) = &mut params.on_custom_attribs {
if let Some(c) = params.on_custom_attribs.as_ref() {
(*c)(parser::CustomAttribsInfo {
pairs: &cell.attribs,
parent_id: id_buttons,
@@ -121,7 +135,7 @@ impl ContextMenu {
});
}
if idx < blueprint.cells.len() - 1 {
if idx < cells.len() - 1 {
inner_parser.parse_template(&doc_params, "Separator", layout, id_buttons, Default::default())?;
}
}
@@ -129,8 +143,8 @@ impl ContextMenu {
}
pub fn tick(&mut self, layout: &mut Layout, parser_state: &mut ParserState) -> anyhow::Result<TickResult> {
if let Some(mut p) = self.pending_open.take() {
self.open_process(&mut p, layout, parser_state)?;
if let Some(p) = self.pending_open.take() {
self.open_process(p, layout, parser_state)?;
let _ = self.tasks.drain();
return Ok(TickResult::Opened);
}

View File

@@ -14,7 +14,9 @@ idmap-derive.workspace = true
log.workspace = true
serde = { workspace = true, features = ["rc"] }
serde_json.workspace = true
strum.workspace = true
xdg.workspace = true
chrono = "0.4.42"
smol = "2.0.2"
wgui = { path = "../wgui/" }

View File

@@ -3,6 +3,7 @@ use std::{collections::HashMap, sync::Arc};
use chrono::Offset;
use idmap::IdMap;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
use crate::{
astr_containers::{AStrMap, AStrSet},
@@ -13,6 +14,22 @@ use crate::{
pub type PwTokenMap = AStrMap<String>;
pub type SerializedWindowStates = HashMap<Arc<str>, OverlayWindowState>;
#[derive(Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
pub enum CaptureMethod {
#[default]
#[serde(alias = "pipewire", alias = "auto")]
#[strum(props(Text = "PipeWire GPU", Tooltip = "APP_SETTINGS.OPTION.PIPEWIRE_HELP"))]
PipeWire,
#[serde(alias = "pw-fallback")]
#[strum(props(Text = "PipeWire CPU", Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP"))]
PwFallback,
#[serde(alias = "screencopy")]
#[strum(props(Text = "ScreenCopy CPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_HELP"))]
ScreenCopy,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct SerializedWindowSet {
pub name: Arc<str>,
@@ -76,10 +93,6 @@ fn def_timezones() -> Vec<String> {
}
}
fn def_auto() -> Arc<str> {
"auto".into()
}
fn def_empty() -> Arc<str> {
"".into()
}
@@ -174,8 +187,8 @@ pub struct GeneralConfig {
#[serde(default)]
pub custom_panels: AStrSet,
#[serde(default = "def_auto")]
pub capture_method: Arc<str>,
#[serde(default)]
pub capture_method: CaptureMethod,
#[serde(default = "def_point7")]
pub xr_click_sensitivity: f32,

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use wlx_common::{
astr_containers::AStrMap,
config::{GeneralConfig, SerializedWindowSet, SerializedWindowStates},
config::{CaptureMethod, GeneralConfig, SerializedWindowSet, SerializedWindowStates},
config_io,
overlays::BackendAttribValue,
};
@@ -138,6 +138,7 @@ pub struct AutoSettings {
pub opaque_background: bool,
pub xwayland_by_default: bool,
pub context_menu_hold_and_release: bool,
pub capture_method: CaptureMethod,
}
fn get_settings_path() -> PathBuf {
@@ -181,6 +182,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
opaque_background: config.opaque_background,
xwayland_by_default: config.xwayland_by_default,
context_menu_hold_and_release: config.context_menu_hold_and_release,
capture_method: config.capture_method,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic

View File

@@ -20,7 +20,7 @@ use wgui::{
parser::{self, AttribPair, CustomAttribsInfoOwned, Fetchable, ParserState},
taffy,
widget::EventResult,
windowing::context_menu::{ContextMenu, OpenParams},
windowing::context_menu::{Blueprint, ContextMenu, OpenParams},
};
use wlx_common::overlays::ToastTopic;
@@ -207,8 +207,9 @@ pub(super) fn setup_custom_button<S: 'static>(
let callback: EventCallback<AppState, S> = match command {
"::ContextMenuOpen" => {
let Some(template_name) = args.next() else {
log::warn!(
"{command} has incorrect arguments. Should be: {command} <context_menu>"
log::error!(
"{:?}: {command} has invalid arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -230,8 +231,10 @@ pub(super) fn setup_custom_button<S: 'static>(
move |_common, data, _app, _| {
context_menu.borrow_mut().open(OpenParams {
on_custom_attribs: Some(on_custom_attribs.clone()),
template_name: template_name.clone(),
template_params: template_params.clone(),
blueprint: Blueprint::Template {
template_name: template_name.clone(),
template_params: template_params.clone(),
},
position: data.metadata.get_mouse_pos_absolute().unwrap(), //want panic
});
Ok(EventResult::Consumed)
@@ -249,14 +252,18 @@ pub(super) fn setup_custom_button<S: 'static>(
}
"::ElementSetDisplay" => {
let (Some(id), Some(value)) = (args.next(), args.next()) else {
log::warn!(
"{command} has incorrect arguments. Should be: {command} <element_id> <display>"
log::error!(
"{:?}: {command} has invalid arguments",
parser_state.path.get_path_buf()
);
return;
};
let Ok(widget_id) = parser_state.data.get_widget_id(id) else {
log::warn!("{command}: no element exists with ID '{id}'");
log::warn!(
"{:?}: {command}: no element exists with ID '{id}'",
parser_state.path.get_path_buf()
);
return;
};
@@ -290,7 +297,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::SetToggle" => {
let arg = args.next().unwrap_or_default();
let Ok(set_idx) = arg.parse() else {
log::error!("{command} has invalid argument: \"{arg}\"");
log::error!(
"{:?}: {command} has invalid argument: \"{arg}\"",
parser_state.path.get_path_buf()
);
return;
};
Box::new(move |_common, data, app, _| {
@@ -306,7 +316,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::SetSwitch" => {
let arg = args.next().unwrap_or_default();
let Ok(set_idx) = arg.parse::<i32>() else {
log::error!("{command} has invalid argument: \"{arg}\"");
log::error!(
"{:?}: {command} has invalid argument: \"{arg}\"",
parser_state.path.get_path_buf()
);
return;
};
let maybe_set = if set_idx < 0 {
@@ -327,7 +340,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayReset" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -346,7 +362,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayToggle" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -366,7 +385,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::OverlayDrop" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -402,7 +424,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::CustomOverlayReload" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -440,7 +465,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::WvrOverlayCloseWindow" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
Box::new(move |_common, data, app, _| {
@@ -463,7 +491,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"::WvrOverlayKillProcess" | "::WvrOverlayTermProcess" => {
let arg: Arc<str> = args.collect::<Vec<_>>().join(" ").into();
if arg.len() < 1 {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has missing arguments",
parser_state.path.get_path_buf()
);
return;
};
@@ -585,7 +616,10 @@ pub(super) fn setup_custom_button<S: 'static>(
}),
"::SendKey" => {
let Some(key) = args.next().and_then(|s| VirtualKey::from_str(s).ok()) else {
log::error!("{command} has bad/missing arguments");
log::error!(
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return;
};
let Some(down) = args.next().and_then(|s| match s.to_lowercase().as_str() {
@@ -593,7 +627,10 @@ pub(super) fn setup_custom_button<S: 'static>(
"up" => Some(false),
_ => None,
}) else {
log::error!("{command} has bad/missing arguments");
log::error!(
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return;
};
Box::new(move |_common, data, app, _| {
@@ -640,7 +677,10 @@ pub(super) fn setup_custom_button<S: 'static>(
use crate::subsystem::osc::parse_osc_value;
let Some(address) = args.next().map(std::string::ToString::to_string) else {
log::error!("{command} has missing arguments");
log::error!(
"{:?}: {command} has bad/missing arguments",
parser_state.path.get_path_buf()
);
return;
};

View File

@@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc};
use std::{any, cell::RefCell, rc::Rc};
use button::setup_custom_button;
use glam::{Affine2, Vec2, vec2};

View File

@@ -18,7 +18,10 @@ use crate::{
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, ui_transform,
},
};
use wlx_common::overlays::{BackendAttrib, BackendAttribValue, MouseTransform, StereoMode};
use wlx_common::{
config::CaptureMethod,
overlays::{BackendAttrib, BackendAttribValue, MouseTransform, StereoMode},
};
use super::capture::{ScreenPipeline, WlxCaptureIn, WlxCaptureOut, receive_callback};
@@ -144,10 +147,12 @@ impl OverlayBackend for ScreenBackend {
.ext_external_memory_dma_buf
&& self.capture.supports_dmbuf();
let allow_dmabuf = &*app.session.config.capture_method != "pw_fallback"
&& &*app.session.config.capture_method != "screencopy";
let capture_method = app.session.config.capture_method;
let capture_method = app.session.config.capture_method.clone();
let allow_dmabuf = !matches!(
capture_method,
CaptureMethod::PwFallback | CaptureMethod::ScreenCopy
);
let dmabuf_formats = if !supports_dmabuf {
log::info!("Capture method does not support DMA-buf");
@@ -158,7 +163,10 @@ impl OverlayBackend for ScreenBackend {
}
&Vec::new()
} else if !allow_dmabuf {
log::info!("Not using DMA-buf capture due to {capture_method}");
log::info!(
"Not using DMA-buf capture due to {}",
capture_method.as_ref()
);
if app.gfx_extras.queue_capture.is_none() {
log::warn!(
"Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."

View File

@@ -6,7 +6,10 @@ use wlx_capture::{
wlr_dmabuf::WlrDmabufCapture,
wlr_screencopy::WlrScreencopyCapture,
};
use wlx_common::{astr_containers::AStrMapExt, config::PwTokenMap};
use wlx_common::{
astr_containers::AStrMapExt,
config::{CaptureMethod, PwTokenMap},
};
use crate::{
overlays::screen::create_screen_from_backend,
@@ -43,18 +46,14 @@ impl ScreenBackend {
#[allow(clippy::useless_let_if_seq)]
pub fn create_screen_renderer_wl(
output: &WlxOutput,
has_wlr_dmabuf: bool,
has_wlr_screencopy: bool,
pw_token_store: &mut PwTokenMap,
app: &mut AppState,
) -> Option<ScreenBackend> {
let mut capture: Option<ScreenBackend> = None;
if (&*app.session.config.capture_method == "wlr-dmabuf") && has_wlr_dmabuf {
log::info!("{}: Using Wlr DMA-Buf", &output.name);
capture = ScreenBackend::new_wlr_dmabuf(output, app);
}
if &*app.session.config.capture_method == "screencopy" && has_wlr_screencopy {
if matches!(app.session.config.capture_method, CaptureMethod::ScreenCopy) && has_wlr_screencopy
{
log::info!("{}: Using Wlr Screencopy Wl-SHM", &output.name);
capture = ScreenBackend::new_wlr_screencopy(output, app);
}
@@ -102,7 +101,6 @@ pub fn create_screens_wayland(wl: &mut WlxClient, app: &mut AppState) -> ScreenC
let mut pw_tokens: PwTokenMap = load_pw_token_config().unwrap_or_default();
let pw_tokens_copy = pw_tokens.clone();
let has_wlr_dmabuf = wl.maybe_wlr_dmabuf_mgr.is_some();
let has_wlr_screencopy = wl.maybe_wlr_screencopy_mgr.is_some();
for (id, output) in &wl.outputs {
@@ -118,13 +116,9 @@ pub fn create_screens_wayland(wl: &mut WlxClient, app: &mut AppState) -> ScreenC
output.logical_pos,
);
if let Some(mut backend) = create_screen_renderer_wl(
output,
has_wlr_dmabuf,
has_wlr_screencopy,
&mut pw_tokens,
app,
) {
if let Some(mut backend) =
create_screen_renderer_wl(output, has_wlr_screencopy, &mut pw_tokens, app)
{
backend.logical_pos = vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32);
backend.logical_size = vec2(output.logical_size.0 as f32, output.logical_size.1 as f32);
backend.mouse_transform_original = output.transform;