Allow output names to select a monitor (#522)
Co-authored-by: elkowar <5300871+elkowar@users.noreply.github.com>
This commit is contained in:
parent
e09c7364a5
commit
535f21f5f9
10 changed files with 120 additions and 20 deletions
|
@ -5,6 +5,10 @@ All notable changes to eww will be listed here, starting at changes since versio
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Features
|
||||
- Add support for output names in X11 to select `:monitor`.
|
||||
|
||||
|
||||
## 0.3.0 (26.05.2022)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -422,6 +422,7 @@ name = "eww_shared_util"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"gdk",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
]
|
||||
|
|
|
@ -20,6 +20,7 @@ use std::{
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use yuck::{
|
||||
config::{
|
||||
monitor::MonitorIdentifier,
|
||||
script_var_definition::ScriptVarDefinition,
|
||||
window_definition::WindowDefinition,
|
||||
window_geometry::{AnchorPoint, WindowGeometry},
|
||||
|
@ -45,7 +46,7 @@ pub enum DaemonCommand {
|
|||
pos: Option<Coords>,
|
||||
size: Option<Coords>,
|
||||
anchor: Option<AnchorPoint>,
|
||||
screen: Option<i32>,
|
||||
screen: Option<MonitorIdentifier>,
|
||||
should_toggle: bool,
|
||||
sender: DaemonResponseSender,
|
||||
},
|
||||
|
@ -294,7 +295,7 @@ impl App {
|
|||
window_name: &str,
|
||||
pos: Option<Coords>,
|
||||
size: Option<Coords>,
|
||||
monitor: Option<i32>,
|
||||
monitor: Option<MonitorIdentifier>,
|
||||
anchor: Option<AnchorPoint>,
|
||||
) -> Result<()> {
|
||||
self.failed_windows.remove(window_name);
|
||||
|
@ -327,7 +328,7 @@ impl App {
|
|||
None,
|
||||
)?;
|
||||
|
||||
let monitor_geometry = get_monitor_geometry(monitor.or(window_def.monitor_number))?;
|
||||
let monitor_geometry = get_monitor_geometry(monitor.or(window_def.monitor.clone()))?;
|
||||
|
||||
let mut eww_window = initialize_window(monitor_geometry, root_widget, window_def, window_scope)?;
|
||||
eww_window.gtk_window.style_context().add_class(&window_name.to_string());
|
||||
|
@ -401,7 +402,7 @@ fn initialize_window(
|
|||
window_scope: ScopeIndex,
|
||||
) -> Result<EwwWindow> {
|
||||
let window = display_backend::initialize_window(&window_def, monitor_geometry)
|
||||
.with_context(|| format!("monitor {} is unavailable", window_def.monitor_number.unwrap()))?;
|
||||
.with_context(|| format!("monitor {} is unavailable", window_def.monitor.clone().unwrap()))?;
|
||||
|
||||
window.set_title(&format!("Eww - {}", window_def.name));
|
||||
window.set_position(gtk::WindowPosition::None);
|
||||
|
@ -476,12 +477,38 @@ fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) {
|
|||
window.set_visual(visual.as_ref());
|
||||
}
|
||||
|
||||
/// Get the monitor geometry of a given monitor number, or the default if none is given
|
||||
fn get_monitor_geometry(n: Option<i32>) -> Result<gdk::Rectangle> {
|
||||
#[allow(deprecated)]
|
||||
/// Get the monitor geometry of a given monitor, or the default if none is given
|
||||
fn get_monitor_geometry(identifier: Option<MonitorIdentifier>) -> Result<gdk::Rectangle> {
|
||||
let display = gdk::Display::default().expect("could not get default display");
|
||||
let monitor = match n {
|
||||
Some(n) => display.monitor(n).with_context(|| format!("Failed to get monitor with index {}", n))?,
|
||||
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());
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
})?
|
||||
}
|
||||
}
|
||||
None => display
|
||||
.primary_monitor()
|
||||
.context("Failed to get primary monitor from GTK. Try explicitly specifying the monitor on your window.")?,
|
||||
|
@ -489,6 +516,29 @@ fn get_monitor_geometry(n: Option<i32>) -> Result<gdk::Rectangle> {
|
|||
Ok(monitor.geometry())
|
||||
}
|
||||
|
||||
/// Returns the [Monitor][gdk::Monitor] structure corresponding to the identifer.
|
||||
/// 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::Numeric(num) => return 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()) {
|
||||
if model == *name {
|
||||
return display.monitor(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn get_window_rectangle(geometry: WindowGeometry, screen_rect: gdk::Rectangle) -> gdk::Rectangle {
|
||||
let (offset_x, offset_y) = geometry.offset.relative_to(screen_rect.width(), screen_rect.height());
|
||||
let (width, height) = geometry.size.relative_to(screen_rect.width(), screen_rect.height());
|
||||
|
|
|
@ -23,9 +23,10 @@ mod platform {
|
|||
// Initialising a layer shell surface
|
||||
gtk_layer_shell::init_for_window(&window);
|
||||
// Sets the monitor where the surface is shown
|
||||
match window_def.monitor_number {
|
||||
Some(index) => {
|
||||
if let Some(monitor) = gdk::Display::default().expect("could not get default display").monitor(index) {
|
||||
match window_def.monitor.clone() {
|
||||
Some(ident) => {
|
||||
let display = gdk::Display::default().expect("could not get default display");
|
||||
if let Some(monitor) = crate::app::get_monitor_from_display(&display, &ident) {
|
||||
gtk_layer_shell::set_monitor(&window, &monitor);
|
||||
} else {
|
||||
return None;
|
||||
|
|
|
@ -3,7 +3,10 @@ use eww_shared_util::VarName;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use simplexpr::dynval::DynVal;
|
||||
use structopt::StructOpt;
|
||||
use yuck::{config::window_geometry::AnchorPoint, value::Coords};
|
||||
use yuck::{
|
||||
config::{monitor::MonitorIdentifier, window_geometry::AnchorPoint},
|
||||
value::Coords,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app,
|
||||
|
@ -91,9 +94,9 @@ pub enum ActionWithServer {
|
|||
/// Name of the window you want to open.
|
||||
window_name: String,
|
||||
|
||||
/// Monitor-index the window should open on
|
||||
/// The identifier of the monitor the window should open on
|
||||
#[structopt(long)]
|
||||
screen: Option<i32>,
|
||||
screen: Option<MonitorIdentifier>,
|
||||
|
||||
/// The position of the window, where it should open. (i.e.: 200x100)
|
||||
#[structopt(short, long)]
|
||||
|
|
|
@ -12,3 +12,4 @@ homepage = "https://github.com/elkowar/eww"
|
|||
serde = {version = "1.0", features = ["derive"]}
|
||||
derive_more = "0.99"
|
||||
ref-cast = "1.0.6"
|
||||
gdk = { version = "*", features = ["v3_22"] }
|
||||
|
|
|
@ -2,6 +2,7 @@ pub mod attributes;
|
|||
pub mod backend_window_options;
|
||||
pub mod config;
|
||||
pub mod file_provider;
|
||||
pub mod monitor;
|
||||
pub mod script_var_definition;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
39
crates/yuck/src/config/monitor.rs
Normal file
39
crates/yuck/src/config/monitor.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use std::{convert::Infallible, fmt, str};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The type of the identifier used to select a monitor
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum MonitorIdentifier {
|
||||
Numeric(i32),
|
||||
Name(String),
|
||||
}
|
||||
|
||||
impl MonitorIdentifier {
|
||||
pub fn is_numeric(&self) -> bool {
|
||||
match self {
|
||||
Self::Numeric(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MonitorIdentifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Numeric(n) => write!(f, "{}", n),
|
||||
Self::Name(n) => write!(f, "{}", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for MonitorIdentifier {
|
||||
type Err = Infallible;
|
||||
|
||||
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())),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ use std::{collections::HashMap, fmt::Display, str::FromStr};
|
|||
use simplexpr::{dynval::DynVal, SimplExpr};
|
||||
|
||||
use crate::{
|
||||
config::monitor::MonitorIdentifier,
|
||||
error::{AstError, AstResult},
|
||||
parser::{
|
||||
ast::Ast,
|
||||
|
@ -20,7 +21,7 @@ pub struct WindowDefinition {
|
|||
pub name: String,
|
||||
pub geometry: Option<WindowGeometry>,
|
||||
pub stacking: WindowStacking,
|
||||
pub monitor_number: Option<i32>,
|
||||
pub monitor: Option<MonitorIdentifier>,
|
||||
pub widget: WidgetUse,
|
||||
pub resizable: bool,
|
||||
pub backend_options: BackendWindowOptions,
|
||||
|
@ -32,14 +33,14 @@ impl FromAstElementContent for WindowDefinition {
|
|||
fn from_tail<I: Iterator<Item = Ast>>(span: Span, mut iter: AstIterator<I>) -> AstResult<Self> {
|
||||
let (_, name) = iter.expect_symbol()?;
|
||||
let mut attrs = iter.expect_key_values()?;
|
||||
let monitor_number = attrs.primitive_optional("monitor")?;
|
||||
let monitor = attrs.primitive_optional("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")?;
|
||||
let backend_options = BackendWindowOptions::from_attrs(&mut attrs)?;
|
||||
let widget = iter.expect_any().and_then(WidgetUse::from_ast)?;
|
||||
iter.expect_done()?;
|
||||
Ok(Self { name, monitor_number, resizable, widget, stacking, geometry, backend_options })
|
||||
Ok(Self { name, monitor, resizable, widget, stacking, geometry, backend_options })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ You can now open your first window by running `eww open example`! Glorious!
|
|||
|
||||
| Property | Description |
|
||||
| ---------: | ------------------------------------------------------------ |
|
||||
| `monitor` | Which monitor this window should be displayed on. |
|
||||
| `monitor` | Which monitor this window should be displayed on. Can be either a number (X11 and Wayland) or an output name (X11 only). |
|
||||
| `geometry` | Geometry of the window. |
|
||||
|
||||
|
||||
|
@ -284,4 +284,3 @@ If you want to separate different widgets even further, you can create a new eww
|
|||
Then, you can tell eww to use that configuration directory by passing _every_ command the `--config /path/to/your/config/dir` flag.
|
||||
Make sure to actually include this in all your `eww` calls, including `eww kill`, `eww logs`, etc.
|
||||
This launches a separate instance of the eww daemon that has separate logs and state from your main eww configuration.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue