fix clippy warnings
This commit is contained in:
1 parent
7b88083020
commit
bdc3ba435f
39 files changed
+825
-688
No files matched your search
@@ -454,13 +454,9 @@ fn open_primary_device(
|
||||
.connected_outputs = scan.connected_outputs;
|
||||
let mut outputs = HashMap::new();
|
||||
for selection in scan.selections {
|
||||
let OutputSelection {
|
||||
connector,
|
||||
crtc,
|
||||
mode,
|
||||
settings,
|
||||
} = selection;
|
||||
let output = create_direct_output(state, &mut drm, &gbm, &renderer, connector, crtc, mode, &settings)?;
|
||||
let crtc = selection.crtc;
|
||||
let mode = selection.mode;
|
||||
let output = create_direct_output(state, &mut drm, &gbm, &renderer, selection)?;
|
||||
state
|
||||
.direct_backend
|
||||
.as_mut()
|
||||
@@ -959,22 +955,9 @@ fn rescan_device(state: &mut Ferese, node: DrmNode) {
|
||||
}
|
||||
|
||||
for (_, selection) in selections {
|
||||
let OutputSelection {
|
||||
connector,
|
||||
crtc,
|
||||
mode,
|
||||
settings,
|
||||
} = selection;
|
||||
match create_direct_output(
|
||||
state,
|
||||
&mut device.drm,
|
||||
&device.gbm,
|
||||
&device.renderer,
|
||||
connector,
|
||||
crtc,
|
||||
mode,
|
||||
&settings,
|
||||
) {
|
||||
let crtc = selection.crtc;
|
||||
let mode = selection.mode;
|
||||
match create_direct_output(state, &mut device.drm, &device.gbm, &device.renderer, selection) {
|
||||
Ok(output) => {
|
||||
state
|
||||
.direct_backend
|
||||
@@ -1419,11 +1402,14 @@ fn create_direct_output(
|
||||
drm: &mut DrmDevice,
|
||||
gbm: &GbmDevice<DrmDeviceFd>,
|
||||
renderer: &GlesRenderer,
|
||||
connector: connector::Info,
|
||||
crtc: crtc::Handle,
|
||||
mode: DrmMode,
|
||||
settings: &OutputSettings,
|
||||
selection: OutputSelection,
|
||||
) -> Result<DirectOutput, Box<dyn Error>> {
|
||||
let OutputSelection {
|
||||
connector,
|
||||
crtc,
|
||||
mode,
|
||||
settings,
|
||||
} = selection;
|
||||
let identity = connector_identity(drm, &connector);
|
||||
let drm_surface = drm.create_surface(crtc, mode, &[connector.handle()])?;
|
||||
let allocator = GbmAllocator::new(gbm.clone(), GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT);
|
||||
@@ -1435,7 +1421,7 @@ fn create_direct_output(
|
||||
)?;
|
||||
// Publish only after DRM/GBM creation succeeds; failed activation must not
|
||||
// leave a phantom output/workspace that could receive evacuated windows.
|
||||
let (output, global) = create_output(state, &connector, mode, identity, settings);
|
||||
let (output, global) = create_output(state, &connector, mode, identity, &settings);
|
||||
let damage_tracker = OutputDamageTracker::from_output(&output);
|
||||
let render_metrics = RenderMetrics::from_environment(output.name());
|
||||
|
||||
|
||||
+33
-33
@@ -96,22 +96,22 @@ impl Config {
|
||||
theme: &ferese_config::theme::ResolvedTheme,
|
||||
inactive_dim: InactiveDimSettings,
|
||||
) -> Result<ThemeSettings, ConfigError> {
|
||||
let mut config = Self::default();
|
||||
config.theme = serde_json::from_value(serde_json::to_value(&theme.tokens).unwrap())
|
||||
.expect("resolved theme matches runtime schema");
|
||||
let config = Self {
|
||||
theme: serde_json::from_value(serde_json::to_value(&theme.tokens).unwrap())
|
||||
.expect("resolved theme matches runtime schema"),
|
||||
..Self::default()
|
||||
};
|
||||
let mut settings = config.theme_settings()?;
|
||||
settings.inactive_dim = inactive_dim;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_config(&self) -> Result<crate::RuntimeConfig, ConfigError> {
|
||||
self.notifications.validate().map_err(ConfigError::InvalidBinding)?;
|
||||
self.desktop_widgets.validate().map_err(ConfigError::InvalidBinding)?;
|
||||
self.notifications.validate().map_err(ConfigError::Binding)?;
|
||||
self.desktop_widgets.validate().map_err(ConfigError::Binding)?;
|
||||
for daemon in &self.autostart {
|
||||
if daemon.command.first().is_none_or(|program| program.trim().is_empty()) {
|
||||
return Err(ConfigError::InvalidBinding(
|
||||
"autostart command must contain a program".into(),
|
||||
));
|
||||
return Err(ConfigError::Binding("autostart command must contain a program".into()));
|
||||
}
|
||||
}
|
||||
let input_settings = self.input_settings()?;
|
||||
@@ -217,29 +217,29 @@ pub enum ConfigError {
|
||||
path: PathBuf,
|
||||
source: ferese_config::Error,
|
||||
},
|
||||
InvalidColumnWidth {
|
||||
ColumnWidth {
|
||||
field: &'static str,
|
||||
value: String,
|
||||
},
|
||||
InvalidAnimationValue {
|
||||
AnimationValue {
|
||||
field: &'static str,
|
||||
value: f64,
|
||||
},
|
||||
InvalidLayoutValue {
|
||||
LayoutValue {
|
||||
field: &'static str,
|
||||
value: f64,
|
||||
},
|
||||
InvalidInputValue {
|
||||
InputValue {
|
||||
field: &'static str,
|
||||
value: String,
|
||||
},
|
||||
InvalidBinding(String),
|
||||
InvalidWindowRule(String),
|
||||
InvalidThemeValue {
|
||||
Binding(String),
|
||||
WindowRule(String),
|
||||
ThemeValue {
|
||||
field: &'static str,
|
||||
value: String,
|
||||
},
|
||||
InvalidOutputProfile(String),
|
||||
OutputProfile(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ConfigError {
|
||||
@@ -249,25 +249,25 @@ impl fmt::Display for ConfigError {
|
||||
Self::Parse { path, source } => {
|
||||
write!(formatter, "failed to parse {}: {source}", path.display())
|
||||
}
|
||||
Self::InvalidColumnWidth { field, value } => write!(
|
||||
Self::ColumnWidth { field, value } => write!(
|
||||
formatter,
|
||||
"invalid scrolling.{field} {value}; expected a positive number or \"full\""
|
||||
),
|
||||
Self::InvalidAnimationValue { field, value } => {
|
||||
Self::AnimationValue { field, value } => {
|
||||
write!(formatter, "invalid animations.{field} value {value}")
|
||||
}
|
||||
Self::InvalidLayoutValue { field, value } => {
|
||||
Self::LayoutValue { field, value } => {
|
||||
write!(formatter, "invalid layout.{field} value {value}")
|
||||
}
|
||||
Self::InvalidInputValue { field, value } => {
|
||||
Self::InputValue { field, value } => {
|
||||
write!(formatter, "invalid input.{field} value {value}")
|
||||
}
|
||||
Self::InvalidBinding(message) => write!(formatter, "invalid binding: {message}"),
|
||||
Self::InvalidWindowRule(message) => write!(formatter, "invalid window rule: {message}"),
|
||||
Self::InvalidThemeValue { field, value } => {
|
||||
Self::Binding(message) => write!(formatter, "invalid binding: {message}"),
|
||||
Self::WindowRule(message) => write!(formatter, "invalid window rule: {message}"),
|
||||
Self::ThemeValue { field, value } => {
|
||||
write!(formatter, "invalid theme.{field} value {value}")
|
||||
}
|
||||
Self::InvalidOutputProfile(message) => {
|
||||
Self::OutputProfile(message) => {
|
||||
write!(formatter, "invalid output profile: {message}")
|
||||
}
|
||||
}
|
||||
@@ -279,21 +279,21 @@ impl Error for ConfigError {
|
||||
match self {
|
||||
#[cfg(test)]
|
||||
Self::Parse { source, .. } => Some(source),
|
||||
Self::InvalidColumnWidth { .. }
|
||||
| Self::InvalidAnimationValue { .. }
|
||||
| Self::InvalidLayoutValue { .. }
|
||||
| Self::InvalidInputValue { .. }
|
||||
| Self::InvalidBinding(_)
|
||||
| Self::InvalidWindowRule(_)
|
||||
| Self::InvalidThemeValue { .. }
|
||||
| Self::InvalidOutputProfile(_) => None,
|
||||
Self::ColumnWidth { .. }
|
||||
| Self::AnimationValue { .. }
|
||||
| Self::LayoutValue { .. }
|
||||
| Self::InputValue { .. }
|
||||
| Self::Binding(_)
|
||||
| Self::WindowRule(_)
|
||||
| Self::ThemeValue { .. }
|
||||
| Self::OutputProfile(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn window_rules(&self) -> Result<Vec<WindowRule>, ConfigError> {
|
||||
window_rules::validate(&self.window_rules).map_err(ConfigError::InvalidWindowRule)
|
||||
window_rules::validate(&self.window_rules).map_err(ConfigError::WindowRule)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ pub(super) fn nonnegative_theme_value(value: f64, field: &'static str) -> Result
|
||||
if value.is_finite() && value >= 0.0 {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
Err(ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
@@ -75,7 +75,7 @@ pub(super) fn finite_theme_value(value: f64, field: &'static str) -> Result<f64,
|
||||
if value.is_finite() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
Err(ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
@@ -86,7 +86,7 @@ pub(super) fn unit_theme_value(value: f64, field: &'static str) -> Result<f64, C
|
||||
if value.is_finite() && (0.0..=1.0).contains(&value) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
Err(ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
@@ -96,7 +96,7 @@ pub(super) fn unit_theme_value(value: f64, field: &'static str) -> Result<f64, C
|
||||
pub(super) fn parse_color(value: &str, field: &'static str) -> Result<RgbaColor, ConfigError> {
|
||||
let digits = value.strip_prefix('#').unwrap_or(value);
|
||||
if !digits.is_ascii() || !matches!(digits.len(), 6 | 8) {
|
||||
return Err(ConfigError::InvalidThemeValue {
|
||||
return Err(ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
});
|
||||
@@ -108,13 +108,13 @@ pub(super) fn parse_color(value: &str, field: &'static str) -> Result<RgbaColor,
|
||||
.zip(parse_channel(4))
|
||||
.map(|((red, green), blue)| (red, green, blue))
|
||||
else {
|
||||
return Err(ConfigError::InvalidThemeValue {
|
||||
return Err(ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
});
|
||||
};
|
||||
let alpha = if digits.len() == 8 {
|
||||
parse_channel(6).ok_or_else(|| ConfigError::InvalidThemeValue {
|
||||
parse_channel(6).ok_or_else(|| ConfigError::ThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
})?
|
||||
|
||||
@@ -23,16 +23,13 @@ impl Config {
|
||||
for configured in &self.bindings {
|
||||
let identity = binding_identity(configured, &keymap)?;
|
||||
if !supplied.insert(identity.clone()) {
|
||||
return Err(ConfigError::InvalidBinding(format!(
|
||||
"duplicate binding {:?}",
|
||||
configured.keys
|
||||
)));
|
||||
return Err(ConfigError::Binding(format!("duplicate binding {:?}", configured.keys)));
|
||||
}
|
||||
|
||||
bindings.retain(|binding| binding_identity_for_runtime(binding) != identity);
|
||||
if configured.disabled {
|
||||
if configured.action.is_some() || configured.argument.is_some() {
|
||||
return Err(ConfigError::InvalidBinding(format!(
|
||||
return Err(ConfigError::Binding(format!(
|
||||
"disabled binding {:?} cannot have an action or argument",
|
||||
configured.keys
|
||||
)));
|
||||
@@ -49,12 +46,10 @@ impl Config {
|
||||
pub(super) fn validate_commands(commands: &HashMap<String, Vec<String>>) -> Result<(), ConfigError> {
|
||||
for (name, argv) in commands {
|
||||
if name.trim().is_empty() {
|
||||
return Err(ConfigError::InvalidBinding("command names cannot be empty".to_owned()));
|
||||
return Err(ConfigError::Binding("command names cannot be empty".to_owned()));
|
||||
}
|
||||
if argv.is_empty() || argv[0].is_empty() {
|
||||
return Err(ConfigError::InvalidBinding(format!(
|
||||
"command {name:?} must contain a program"
|
||||
)));
|
||||
return Err(ConfigError::Binding(format!("command {name:?} must contain a program")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +69,7 @@ pub(crate) fn physical_keymap(input: &InputSettings) -> Result<xkb::Keymap, Conf
|
||||
options,
|
||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
||||
)
|
||||
.ok_or_else(|| ConfigError::InvalidBinding("failed to compile the XKB keymap".to_owned()))
|
||||
.ok_or_else(|| ConfigError::Binding("failed to compile the XKB keymap".to_owned()))
|
||||
}
|
||||
|
||||
pub(super) fn parse_binding(
|
||||
@@ -83,7 +78,7 @@ pub(super) fn parse_binding(
|
||||
keymap: &xkb::Keymap,
|
||||
) -> Result<Binding, ConfigError> {
|
||||
if configured.disabled {
|
||||
return Err(ConfigError::InvalidBinding(format!(
|
||||
return Err(ConfigError::Binding(format!(
|
||||
"disabled binding {:?} cannot be executed",
|
||||
configured.keys
|
||||
)));
|
||||
@@ -93,7 +88,7 @@ pub(super) fn parse_binding(
|
||||
let action = configured
|
||||
.action
|
||||
.as_deref()
|
||||
.ok_or_else(|| ConfigError::InvalidBinding(format!("binding {:?} has no action", configured.keys)))?;
|
||||
.ok_or_else(|| ConfigError::Binding(format!("binding {:?} has no action", configured.keys)))?;
|
||||
let action = parse_action(action, configured.argument.as_deref(), commands)?;
|
||||
let trigger = match identity.match_mode {
|
||||
BindingMatch::Keysym => BindingTrigger::Keysym(identity.key),
|
||||
@@ -123,7 +118,7 @@ pub(super) fn binding_identity(
|
||||
let (modifiers, key) = parse_chord(&configured.keys)?;
|
||||
if let Some((fingers, direction)) = parse_swipe(&key) {
|
||||
if configured.match_mode == BindingMatch::Physical || modifiers != BindingModifiers::default() {
|
||||
return Err(ConfigError::InvalidBinding(
|
||||
return Err(ConfigError::Binding(
|
||||
"swipes cannot use keyboard modifiers or physical key matching".into(),
|
||||
));
|
||||
}
|
||||
@@ -139,13 +134,13 @@ pub(super) fn binding_identity(
|
||||
.key_by_name(&key.to_ascii_uppercase())
|
||||
.map(Keycode::raw)
|
||||
.ok_or_else(|| {
|
||||
ConfigError::InvalidBinding(format!(
|
||||
ConfigError::Binding(format!(
|
||||
"unknown XKB physical key name {key:?} in {:?}",
|
||||
configured.keys
|
||||
))
|
||||
})?,
|
||||
BindingMatch::Swipe => {
|
||||
return Err(ConfigError::InvalidBinding(
|
||||
return Err(ConfigError::Binding(
|
||||
"gesture keys must use Swipe3Left/Right/Up/Down (3–5 fingers)".into(),
|
||||
));
|
||||
}
|
||||
@@ -178,7 +173,7 @@ pub(super) fn parse_chord(chord: &str) -> Result<(BindingModifiers, String), Con
|
||||
|
||||
for component in chord.split('+').map(str::trim) {
|
||||
if component.is_empty() {
|
||||
return Err(ConfigError::InvalidBinding(format!("invalid key chord {chord:?}")));
|
||||
return Err(ConfigError::Binding(format!("invalid key chord {chord:?}")));
|
||||
}
|
||||
|
||||
let slot = match component.to_ascii_lowercase().as_str() {
|
||||
@@ -190,17 +185,17 @@ pub(super) fn parse_chord(chord: &str) -> Result<(BindingModifiers, String), Con
|
||||
};
|
||||
if let Some(slot) = slot {
|
||||
if *slot {
|
||||
return Err(ConfigError::InvalidBinding(format!("duplicate modifier in {chord:?}")));
|
||||
return Err(ConfigError::Binding(format!("duplicate modifier in {chord:?}")));
|
||||
}
|
||||
*slot = true;
|
||||
} else if key.replace(component.to_owned()).is_some() {
|
||||
return Err(ConfigError::InvalidBinding(format!(
|
||||
return Err(ConfigError::Binding(format!(
|
||||
"key chord {chord:?} contains more than one key"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let key = key.ok_or_else(|| ConfigError::InvalidBinding(format!("key chord {chord:?} does not contain a key")))?;
|
||||
let key = key.ok_or_else(|| ConfigError::Binding(format!("key chord {chord:?} does not contain a key")))?;
|
||||
Ok((modifiers, key))
|
||||
}
|
||||
|
||||
@@ -236,7 +231,7 @@ pub(super) fn parse_keysym(name: &str) -> Result<u32, ConfigError> {
|
||||
symbol = xkb::keysym_from_name(&normalized, xkb::KEYSYM_CASE_INSENSITIVE);
|
||||
}
|
||||
if symbol.raw() == keysyms::KEY_NoSymbol {
|
||||
Err(ConfigError::InvalidBinding(format!("unknown keysym {name:?}")))
|
||||
Err(ConfigError::Binding(format!("unknown keysym {name:?}")))
|
||||
} else {
|
||||
Ok(symbol.raw())
|
||||
}
|
||||
@@ -249,7 +244,7 @@ pub(super) fn parse_action(
|
||||
) -> Result<BindingAction, ConfigError> {
|
||||
let no_argument = || {
|
||||
if argument.is_some() {
|
||||
Err(ConfigError::InvalidBinding(format!(
|
||||
Err(ConfigError::Binding(format!(
|
||||
"action {action:?} does not accept an argument"
|
||||
)))
|
||||
} else {
|
||||
@@ -257,7 +252,7 @@ pub(super) fn parse_action(
|
||||
}
|
||||
};
|
||||
let required_argument =
|
||||
|| argument.ok_or_else(|| ConfigError::InvalidBinding(format!("action {action:?} requires an argument")));
|
||||
|| argument.ok_or_else(|| ConfigError::Binding(format!("action {action:?} requires an argument")));
|
||||
|
||||
match action {
|
||||
"none" => {
|
||||
@@ -276,7 +271,7 @@ pub(super) fn parse_action(
|
||||
let command = required_argument()?;
|
||||
let argv = commands
|
||||
.get(command)
|
||||
.ok_or_else(|| ConfigError::InvalidBinding(format!("unknown command {command:?}")))?;
|
||||
.ok_or_else(|| ConfigError::Binding(format!("unknown command {command:?}")))?;
|
||||
Ok(BindingAction::Spawn(argv.clone()))
|
||||
}
|
||||
"close" => {
|
||||
@@ -332,7 +327,7 @@ pub(super) fn parse_action(
|
||||
no_argument()?;
|
||||
Ok(BindingAction::ToggleKeybindingGuide)
|
||||
}
|
||||
_ => Err(ConfigError::InvalidBinding(format!("unknown action {action:?}"))),
|
||||
_ => Err(ConfigError::Binding(format!("unknown action {action:?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,14 +337,14 @@ pub(super) fn parse_direction(argument: &str) -> Result<Direction, ConfigError>
|
||||
"right" => Ok(Direction::Right),
|
||||
"up" => Ok(Direction::Up),
|
||||
"down" => Ok(Direction::Down),
|
||||
_ => Err(ConfigError::InvalidBinding(format!("invalid direction {argument:?}"))),
|
||||
_ => Err(ConfigError::Binding(format!("invalid direction {argument:?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_workspace(argument: &str) -> Result<u8, ConfigError> {
|
||||
match argument.parse() {
|
||||
Ok(workspace) if workspace > 0 => Ok(workspace),
|
||||
_ => Err(ConfigError::InvalidBinding(format!("invalid workspace {argument:?}"))),
|
||||
_ => Err(ConfigError::Binding(format!("invalid workspace {argument:?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +454,7 @@ pub enum BindingAction {
|
||||
impl Binding {
|
||||
pub(crate) fn portal_trigger(chord: &str) -> Result<Self, ConfigError> {
|
||||
if chord.len() > 256 {
|
||||
return Err(ConfigError::InvalidBinding("Shortcut is too long".into()));
|
||||
return Err(ConfigError::Binding("Shortcut is too long".into()));
|
||||
}
|
||||
let parts = chord.split('+').collect::<Vec<_>>();
|
||||
if parts
|
||||
@@ -468,7 +463,7 @@ impl Binding {
|
||||
.count()
|
||||
> 1
|
||||
{
|
||||
return Err(ConfigError::InvalidBinding("Duplicate NUM modifier".into()));
|
||||
return Err(ConfigError::Binding("Duplicate NUM modifier".into()));
|
||||
}
|
||||
let chord = parts
|
||||
.into_iter()
|
||||
@@ -482,7 +477,7 @@ impl Binding {
|
||||
&& matches!(symbol, keysyms::KEY_Escape | keysyms::KEY_F1..=keysyms::KEY_F12))
|
||||
|| (keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12).contains(&symbol)
|
||||
{
|
||||
return Err(ConfigError::InvalidBinding("Reserved system shortcut".into()));
|
||||
return Err(ConfigError::Binding("Reserved system shortcut".into()));
|
||||
}
|
||||
Ok(Self {
|
||||
modifiers,
|
||||
|
||||
@@ -3,26 +3,26 @@ use super::*;
|
||||
impl Config {
|
||||
pub fn input_settings(&self) -> Result<InputSettings, ConfigError> {
|
||||
if self.input.xkb_layout.trim().is_empty() {
|
||||
return Err(ConfigError::InvalidInputValue {
|
||||
return Err(ConfigError::InputValue {
|
||||
field: "xkb_layout",
|
||||
value: self.input.xkb_layout.clone(),
|
||||
});
|
||||
}
|
||||
if self.input.repeat_rate <= 0 {
|
||||
return Err(ConfigError::InvalidInputValue {
|
||||
return Err(ConfigError::InputValue {
|
||||
field: "repeat_rate",
|
||||
value: self.input.repeat_rate.to_string(),
|
||||
});
|
||||
}
|
||||
if self.input.repeat_delay_ms < 0 {
|
||||
return Err(ConfigError::InvalidInputValue {
|
||||
return Err(ConfigError::InputValue {
|
||||
field: "repeat_delay_ms",
|
||||
value: self.input.repeat_delay_ms.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if !(16..=1000).contains(&self.input.touchpad.swipe_threshold) {
|
||||
return Err(ConfigError::InvalidInputValue {
|
||||
return Err(ConfigError::InputValue {
|
||||
field: "touchpad.swipe_threshold",
|
||||
value: self.input.touchpad.swipe_threshold.to_string(),
|
||||
});
|
||||
@@ -56,7 +56,7 @@ impl Config {
|
||||
let stiffness = positive_animation_value(self.animations.spring.stiffness, "spring.stiffness")?;
|
||||
let damping = self.animations.spring.damping;
|
||||
if !damping.is_finite() || damping < 0.0 {
|
||||
return Err(ConfigError::InvalidAnimationValue {
|
||||
return Err(ConfigError::AnimationValue {
|
||||
field: "spring.damping",
|
||||
value: damping,
|
||||
});
|
||||
@@ -92,7 +92,7 @@ pub(super) fn positive_animation_value(value: f64, field: &'static str) -> Resul
|
||||
if value.is_finite() && value > 0.0 {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidAnimationValue { field, value })
|
||||
Err(ConfigError::AnimationValue { field, value })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ pub(super) fn nonnegative_layout_value(value: f64, field: &'static str) -> Resul
|
||||
if value.is_finite() && value >= 0.0 {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidLayoutValue { field, value })
|
||||
Err(ConfigError::LayoutValue { field, value })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ pub(super) fn parse_column_width(value: &ColumnWidthValue, field: &'static str)
|
||||
match value {
|
||||
ColumnWidthValue::Proportion(value) if value.is_finite() && *value > 0.0 => Ok(ColumnWidth::Proportion(*value)),
|
||||
ColumnWidthValue::Named(value) if value.eq_ignore_ascii_case("full") => Ok(ColumnWidth::Full),
|
||||
value => Err(ConfigError::InvalidColumnWidth {
|
||||
value => Err(ConfigError::ColumnWidth {
|
||||
field,
|
||||
value: match value {
|
||||
ColumnWidthValue::Proportion(value) => value.to_string(),
|
||||
|
||||
@@ -9,19 +9,13 @@ impl Config {
|
||||
.map(|profile| {
|
||||
let name = profile.name.trim();
|
||||
if name.is_empty() {
|
||||
return Err(ConfigError::InvalidOutputProfile(
|
||||
"profile names cannot be empty".to_owned(),
|
||||
));
|
||||
return Err(ConfigError::OutputProfile("profile names cannot be empty".to_owned()));
|
||||
}
|
||||
if !names.insert(name.to_owned()) {
|
||||
return Err(ConfigError::InvalidOutputProfile(format!(
|
||||
"duplicate profile name {name:?}"
|
||||
)));
|
||||
return Err(ConfigError::OutputProfile(format!("duplicate profile name {name:?}")));
|
||||
}
|
||||
if profile.outputs.is_empty() {
|
||||
return Err(ConfigError::InvalidOutputProfile(format!(
|
||||
"profile {name:?} has no outputs"
|
||||
)));
|
||||
return Err(ConfigError::OutputProfile(format!("profile {name:?} has no outputs")));
|
||||
}
|
||||
|
||||
let mut matchers = HashSet::new();
|
||||
@@ -31,17 +25,17 @@ impl Config {
|
||||
.map(|output| {
|
||||
let matcher = output.matcher.trim();
|
||||
if matcher.is_empty() {
|
||||
return Err(ConfigError::InvalidOutputProfile(format!(
|
||||
return Err(ConfigError::OutputProfile(format!(
|
||||
"profile {name:?} contains an empty output matcher"
|
||||
)));
|
||||
}
|
||||
if !matchers.insert(matcher.to_owned()) {
|
||||
return Err(ConfigError::InvalidOutputProfile(format!(
|
||||
return Err(ConfigError::OutputProfile(format!(
|
||||
"profile {name:?} repeats output matcher {matcher:?}"
|
||||
)));
|
||||
}
|
||||
if !output.scale.is_finite() || output.scale <= 0.0 {
|
||||
return Err(ConfigError::InvalidOutputProfile(format!(
|
||||
return Err(ConfigError::OutputProfile(format!(
|
||||
"profile {name:?} output {matcher:?} has invalid scale {}",
|
||||
output.scale
|
||||
)));
|
||||
@@ -56,7 +50,7 @@ impl Config {
|
||||
.as_deref()
|
||||
.map(parse_output_mode)
|
||||
.transpose()
|
||||
.map_err(ConfigError::InvalidOutputProfile)?,
|
||||
.map_err(ConfigError::OutputProfile)?,
|
||||
scale: output.scale,
|
||||
transform: output.transform,
|
||||
position: output.position,
|
||||
|
||||
+106
-103
@@ -215,10 +215,13 @@ impl InputCapture {
|
||||
}
|
||||
for barrier in barriers {
|
||||
let kind = edge(&barrier, &self.zones);
|
||||
if zone_set != self.zone_set || counts[&barrier.id] != 1 || kind.is_none() {
|
||||
failed.push(barrier.id);
|
||||
if zone_set == self.zone_set
|
||||
&& counts[&barrier.id] == 1
|
||||
&& let Some(kind) = kind
|
||||
{
|
||||
accepted.push((barrier, kind));
|
||||
} else {
|
||||
accepted.push((barrier, kind.unwrap()));
|
||||
failed.push(barrier.id);
|
||||
}
|
||||
}
|
||||
self.sessions.get_mut(&id).unwrap().barriers = accepted;
|
||||
@@ -418,6 +421,106 @@ impl InputCapture {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Ferese {
|
||||
pub(crate) fn refresh_input_capture_zones(&mut self) {
|
||||
if !self.input_capture.has_sessions() {
|
||||
return;
|
||||
}
|
||||
let active = self.input_capture.active();
|
||||
let zones = self
|
||||
.space
|
||||
.outputs()
|
||||
.filter_map(|output| {
|
||||
let rect = self.space.output_geometry(output)?;
|
||||
Some(Zone {
|
||||
width: u32::try_from(rect.size.w).ok()?,
|
||||
height: u32::try_from(rect.size.h).ok()?,
|
||||
x: rect.loc.x,
|
||||
y: rect.loc.y,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
self.input_capture.update_zones(zones);
|
||||
if active && !self.input_capture.active() {
|
||||
self.restore_input_capture_focus();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn restore_input_capture_focus(&mut self) {
|
||||
self.input_capture.restore_focus = false;
|
||||
self.cursor_redraw_pending = true;
|
||||
if !self.session_lock.active {
|
||||
self.restore_keyboard_focus();
|
||||
if let Some(pointer) = self.seat.get_pointer() {
|
||||
let location = pointer.current_location();
|
||||
pointer.motion(
|
||||
self,
|
||||
self.surface_under(location),
|
||||
&smithay::input::pointer::MotionEvent {
|
||||
location,
|
||||
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn capture_pointer_motion(
|
||||
&mut self,
|
||||
from: smithay::utils::Point<f64, smithay::utils::Logical>,
|
||||
to: smithay::utils::Point<f64, smithay::utils::Logical>,
|
||||
absolute: bool,
|
||||
) -> bool {
|
||||
let pointer = self.seat.get_pointer().expect("seat has pointer");
|
||||
let active = self.input_capture.active();
|
||||
let allowed = !self.session_lock.active
|
||||
&& !self.overview.is_presenting()
|
||||
&& !pointer.is_grabbed()
|
||||
&& !self.swipe.active()
|
||||
&& self.seat.get_keyboard().is_none_or(|keyboard| !keyboard.is_grabbed())
|
||||
&& self.seat.get_touch().is_none_or(|touch| !touch.is_grabbed())
|
||||
&& !self.seat.keyboard_shortcuts_inhibited();
|
||||
if !self.input_capture.motion((from.x, from.y), (to.x, to.y), allowed) {
|
||||
return false;
|
||||
}
|
||||
let delta = if absolute {
|
||||
self.input_capture.absolute_delta((to.x, to.y))
|
||||
} else {
|
||||
(to.x - from.x, to.y - from.y)
|
||||
};
|
||||
if !active {
|
||||
self.cursor_redraw_pending = true;
|
||||
pointer.motion(
|
||||
self,
|
||||
None,
|
||||
&smithay::input::pointer::MotionEvent {
|
||||
location: from,
|
||||
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
if self.input_capture.captures(1) {
|
||||
let keyboard = self.seat.get_keyboard().expect("seat has keyboard");
|
||||
let keys = keyboard
|
||||
.pressed_keys()
|
||||
.iter()
|
||||
.filter(|key| !self.intercepted_keys.contains(key))
|
||||
.map(|key| key.raw().saturating_sub(8))
|
||||
.collect::<Vec<_>>();
|
||||
keyboard.set_focus(self, None, smithay::utils::SERIAL_COUNTER.next_serial());
|
||||
self.input_capture.push(json!({"type":"keys", "keys":keys}));
|
||||
}
|
||||
} else if self.input_capture.captures(2) {
|
||||
self.input_capture
|
||||
.push(json!({"type":"motion", "x":delta.0, "y":delta.1}));
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -556,103 +659,3 @@ mod tests {
|
||||
assert!(capture.sessions[&id].barriers.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Ferese {
|
||||
pub(crate) fn refresh_input_capture_zones(&mut self) {
|
||||
if !self.input_capture.has_sessions() {
|
||||
return;
|
||||
}
|
||||
let active = self.input_capture.active();
|
||||
let zones = self
|
||||
.space
|
||||
.outputs()
|
||||
.filter_map(|output| {
|
||||
let rect = self.space.output_geometry(output)?;
|
||||
Some(Zone {
|
||||
width: u32::try_from(rect.size.w).ok()?,
|
||||
height: u32::try_from(rect.size.h).ok()?,
|
||||
x: rect.loc.x,
|
||||
y: rect.loc.y,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
self.input_capture.update_zones(zones);
|
||||
if active && !self.input_capture.active() {
|
||||
self.restore_input_capture_focus();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn restore_input_capture_focus(&mut self) {
|
||||
self.input_capture.restore_focus = false;
|
||||
self.cursor_redraw_pending = true;
|
||||
if !self.session_lock.active {
|
||||
self.restore_keyboard_focus();
|
||||
if let Some(pointer) = self.seat.get_pointer() {
|
||||
let location = pointer.current_location();
|
||||
pointer.motion(
|
||||
self,
|
||||
self.surface_under(location),
|
||||
&smithay::input::pointer::MotionEvent {
|
||||
location,
|
||||
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn capture_pointer_motion(
|
||||
&mut self,
|
||||
from: smithay::utils::Point<f64, smithay::utils::Logical>,
|
||||
to: smithay::utils::Point<f64, smithay::utils::Logical>,
|
||||
absolute: bool,
|
||||
) -> bool {
|
||||
let pointer = self.seat.get_pointer().expect("seat has pointer");
|
||||
let active = self.input_capture.active();
|
||||
let allowed = !self.session_lock.active
|
||||
&& !self.overview.is_presenting()
|
||||
&& !pointer.is_grabbed()
|
||||
&& !self.swipe.active()
|
||||
&& self.seat.get_keyboard().is_none_or(|keyboard| !keyboard.is_grabbed())
|
||||
&& self.seat.get_touch().is_none_or(|touch| !touch.is_grabbed())
|
||||
&& !self.seat.keyboard_shortcuts_inhibited();
|
||||
if !self.input_capture.motion((from.x, from.y), (to.x, to.y), allowed) {
|
||||
return false;
|
||||
}
|
||||
let delta = if absolute {
|
||||
self.input_capture.absolute_delta((to.x, to.y))
|
||||
} else {
|
||||
(to.x - from.x, to.y - from.y)
|
||||
};
|
||||
if !active {
|
||||
self.cursor_redraw_pending = true;
|
||||
pointer.motion(
|
||||
self,
|
||||
None,
|
||||
&smithay::input::pointer::MotionEvent {
|
||||
location: from,
|
||||
serial: smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(self);
|
||||
if self.input_capture.captures(1) {
|
||||
let keyboard = self.seat.get_keyboard().expect("seat has keyboard");
|
||||
let keys = keyboard
|
||||
.pressed_keys()
|
||||
.iter()
|
||||
.filter(|key| !self.intercepted_keys.contains(key))
|
||||
.map(|key| key.raw().saturating_sub(8))
|
||||
.collect::<Vec<_>>();
|
||||
keyboard.set_focus(self, None, smithay::utils::SERIAL_COUNTER.next_serial());
|
||||
self.input_capture.push(json!({"type":"keys", "keys":keys}));
|
||||
}
|
||||
} else if self.input_capture.captures(2) {
|
||||
self.input_capture
|
||||
.push(json!({"type":"motion", "x":delta.0, "y":delta.1}));
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -104,11 +104,10 @@ pub(crate) fn capture_window_frame(
|
||||
pixel.swap(0, 2);
|
||||
let alpha = pixel[3] as u16;
|
||||
for channel in &mut pixel[..3] {
|
||||
*channel = if alpha == 0 {
|
||||
0
|
||||
} else {
|
||||
((*channel as u16 * 255 + alpha / 2) / alpha).min(255) as u8
|
||||
};
|
||||
*channel = (*channel as u16 * 255 + alpha / 2)
|
||||
.checked_div(alpha)
|
||||
.unwrap_or(0)
|
||||
.min(255) as u8;
|
||||
}
|
||||
}
|
||||
Ok(crate::handlers::screenshot::CaptureBuffer {
|
||||
|
||||
@@ -37,7 +37,19 @@ pub(super) fn append_material_surface(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, (rect, radius))| {
|
||||
material_element(state, renderer, output, surface, *rect, *radius, index, geometry, 1.0)
|
||||
material_element(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
surface,
|
||||
MaterialSurface {
|
||||
geometry: *rect,
|
||||
radius: *radius,
|
||||
index,
|
||||
capture_geometry: geometry,
|
||||
alpha: 1.0,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
if let Some(buffers) = state.material_buffers.get_mut(surface) {
|
||||
@@ -89,17 +101,28 @@ pub(super) fn append_material_surface(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct MaterialSurface {
|
||||
pub geometry: Rectangle<i32, Logical>,
|
||||
pub radius: f32,
|
||||
pub index: usize,
|
||||
pub capture_geometry: Rectangle<i32, Logical>,
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
pub(super) fn material_element(
|
||||
state: &mut Ferese,
|
||||
renderer: &mut GlesRenderer,
|
||||
output: &Output,
|
||||
surface: &smithay::reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
radius: f32,
|
||||
index: usize,
|
||||
capture_geometry: Rectangle<i32, Logical>,
|
||||
alpha: f32,
|
||||
surface_geometry: MaterialSurface,
|
||||
) -> Option<(AnimatedWindowRenderElement, AnimatedWindowRenderElement)> {
|
||||
let MaterialSurface {
|
||||
geometry,
|
||||
radius,
|
||||
index,
|
||||
capture_geometry,
|
||||
alpha,
|
||||
} = surface_geometry;
|
||||
let (role, generation) = crate::effects::surface_role(surface)?;
|
||||
let material = crate::effects::resolve_material(
|
||||
role,
|
||||
|
||||
@@ -527,7 +527,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let size = (32, 32).into();
|
||||
let mut pixels = vec![128u8; 32 * 32 * 4];
|
||||
for pixel in pixels.chunks_exact_mut(4) {
|
||||
for pixel in pixels.as_chunks_mut::<4>().0 {
|
||||
pixel[3] = 255;
|
||||
}
|
||||
let blurred = renderer.import_memory(&pixels, Fourcc::Abgr8888, size, false).unwrap();
|
||||
|
||||
@@ -306,11 +306,13 @@ pub(crate) fn output_elements(
|
||||
renderer,
|
||||
output,
|
||||
surface,
|
||||
constrain,
|
||||
window_radius as f32,
|
||||
0,
|
||||
constrain,
|
||||
close_alpha,
|
||||
MaterialSurface {
|
||||
geometry: constrain,
|
||||
radius: window_radius as f32,
|
||||
index: 0,
|
||||
capture_geometry: constrain,
|
||||
alpha: close_alpha,
|
||||
},
|
||||
)
|
||||
{
|
||||
elements.push(background);
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Default for IdleSettings {
|
||||
impl IdleSettings {
|
||||
pub(crate) fn validate(self) -> Result<Self, crate::config::ConfigError> {
|
||||
if self.dim_after_seconds > 86400 || self.sleep_after_seconds > 86400 {
|
||||
return Err(crate::config::ConfigError::InvalidInputValue {
|
||||
return Err(crate::config::ConfigError::InputValue {
|
||||
field: "lock_screen.idle_delay",
|
||||
value: "must be between 0 and 86400 seconds".into(),
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user