Improve flexibility of monitor configuration (#1003)

* Allow named monitors on wayland

* Allow list of monitor matchers
This commit is contained in:
ElKowar 2023-12-20 16:59:39 +01:00 committed by GitHub
parent fff40ce1a7
commit 4f1f853b5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 33 deletions

View File

@ -20,6 +20,8 @@ All notable changes to eww will be listed here, starting at changes since versio
- Add trigonometric functions (`sin`, `cos`, `tan`, `cot`) and degree/radian conversions (`degtorad`, `radtodeg`) (By: end-4)
- Add `substring` function to simplexpr
- Add `--duration` flag to `eww open`
- Add support for referring to monitor with `<primary>`
- Add support for multiple matchers in `monitor` field
## [0.4.0] (04.09.2022)

View File

@ -560,31 +560,16 @@ fn get_monitor_geometry(identifier: Option<MonitorIdentifier>) -> Result<gdk::Re
let monitor = match identifier {
Some(ident) => {
let mon = get_monitor_from_display(&display, &ident);
#[cfg(feature = "x11")]
{
mon.with_context(|| {
let head = format!("Failed to get monitor {}\nThe available monitors are:", ident);
let mut body = String::new();
for m in 0..display.n_monitors() {
if let Some(model) = display.monitor(m).and_then(|x| x.model()) {
body.push_str(format!("\n\t[{}] {}", m, model).as_str());
}
mon.with_context(|| {
let head = format!("Failed to get monitor {}\nThe available monitors are:", ident);
let mut body = String::new();
for m in 0..display.n_monitors() {
if let Some(model) = display.monitor(m).and_then(|x| x.model()) {
body.push_str(format!("\n\t[{}] {}", m, model).as_str());
}
format!("{}{}", head, body)
})?
}
#[cfg(not(feature = "x11"))]
{
mon.with_context(|| {
if ident.is_numeric() {
format!("Failed to get monitor {}", ident)
} else {
format!("Using ouput names (\"{}\" in the configuration) is not supported outside of x11 yet", ident)
}
})?
}
}
format!("{}{}", head, body)
})?
}
None => display
.primary_monitor()
@ -597,12 +582,16 @@ fn get_monitor_geometry(identifier: Option<MonitorIdentifier>) -> Result<gdk::Re
/// Outside of x11, only [MonitorIdentifier::Numeric] is supported
pub fn get_monitor_from_display(display: &gdk::Display, identifier: &MonitorIdentifier) -> Option<gdk::Monitor> {
match identifier {
MonitorIdentifier::List(list) => {
for ident in list {
if let Some(monitor) = get_monitor_from_display(display, ident) {
return Some(monitor);
}
}
None
}
MonitorIdentifier::Primary => display.primary_monitor(),
MonitorIdentifier::Numeric(num) => display.monitor(*num),
#[cfg(not(feature = "x11"))]
MonitorIdentifier::Name(_) => return None,
#[cfg(feature = "x11")]
MonitorIdentifier::Name(name) => {
for m in 0..display.n_monitors() {
if let Some(model) = display.monitor(m).and_then(|x| x.model()) {

View File

@ -5,8 +5,10 @@ use serde::{Deserialize, Serialize};
/// The type of the identifier used to select a monitor
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MonitorIdentifier {
List(Vec<MonitorIdentifier>),
Numeric(i32),
Name(String),
Primary,
}
impl MonitorIdentifier {
@ -18,8 +20,10 @@ impl MonitorIdentifier {
impl fmt::Display for MonitorIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::List(l) => write!(f, "[{}]", l.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(" ")),
Self::Numeric(n) => write!(f, "{}", n),
Self::Name(n) => write!(f, "{}", n),
Self::Primary => write!(f, "<primary>"),
}
}
}
@ -30,7 +34,13 @@ impl str::FromStr for MonitorIdentifier {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse::<i32>() {
Ok(n) => Ok(Self::Numeric(n)),
Err(_) => Ok(Self::Name(s.to_owned())),
Err(_) => {
if &s.to_lowercase() == "<primary>" {
Ok(Self::Primary)
} else {
Ok(Self::Name(s.to_owned()))
}
}
}
}
}

View File

@ -1,4 +1,4 @@
use std::fmt::Display;
use std::{fmt::Display, str::FromStr};
use crate::{
config::monitor::MonitorIdentifier,
@ -24,13 +24,22 @@ pub struct WindowDefinition {
pub backend_options: BackendWindowOptions,
}
impl FromAst for MonitorIdentifier {
fn from_ast(x: Ast) -> DiagResult<Self> {
match x {
Ast::Array(_, x) => Ok(Self::List(x.into_iter().map(MonitorIdentifier::from_ast).collect::<DiagResult<_>>()?)),
other => Ok(Self::from_str(&String::from_ast(other)?).unwrap()),
}
}
}
impl FromAstElementContent for WindowDefinition {
const ELEMENT_NAME: &'static str = "defwindow";
fn from_tail<I: Iterator<Item = Ast>>(_span: Span, mut iter: AstIterator<I>) -> DiagResult<Self> {
let (_, name) = iter.expect_symbol()?;
let mut attrs = iter.expect_key_values()?;
let monitor = attrs.primitive_optional("monitor")?;
let monitor = attrs.ast_optional::<MonitorIdentifier>("monitor")?;
let resizable = attrs.primitive_optional("resizable")?.unwrap_or(true);
let stacking = attrs.primitive_optional("stacking")?.unwrap_or(WindowStacking::Foreground);
let geometry = attrs.ast_optional("geometry")?;

View File

@ -71,6 +71,8 @@ impl Ast {
as_func!(AstType::List, as_list as_list_ref<Vec<Ast>> = Ast::List(_, x) => x);
as_func!(AstType::Array, as_array as_array_ref<Vec<Ast>> = Ast::Array(_, x) => x);
pub fn expr_type(&self) -> AstType {
match self {
Ast::List(..) => AstType::List,

View File

@ -50,10 +50,20 @@ You can now open your first window by running `eww open example`! Glorious!
| Property | Description |
| ---------: | ------------------------------------------------------------ |
| `monitor` | Which monitor this window should be displayed on. Can be either a number (X11 and Wayland) or an output name (X11 only). |
| `monitor` | Which monitor this window should be displayed on. See below for details.|
| `geometry` | Geometry of the window. |
**`monitor`-property**
This field can be:
- the string `<primary>`, in which case eww tries to identify the primary display (which may fail, especially on wayland)
- an integer, declaring the monitor index
- the name of the monitor
- an array of monitor matchers, such as: `["<primary>" "HDMI-A-1" "PHL 345B1C" 0]`. Eww will try to find a match in order, allowing you to specify fallbacks.
**`geometry`-properties**
| Property | Description |