diff --git a/Cargo.lock b/Cargo.lock
index f9a2653..ca5454d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -263,6 +263,7 @@ dependencies = [
"futures-util",
"gdk",
"gdk-pixbuf",
+ "gdkx11",
"gio",
"glib",
"grass",
@@ -273,7 +274,7 @@ dependencies = [
"libc",
"log",
"maplit",
- "nix",
+ "nix 0.19.1",
"num",
"pretty_assertions",
"pretty_env_logger",
@@ -287,6 +288,7 @@ dependencies = [
"tokio-stream",
"tokio-util",
"unescape",
+ "x11rb",
]
[[package]]
@@ -461,6 +463,55 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "gdkx11"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b89606baa221f9b8d8aa81924fd448c6b107d20de949f0fbf9a4ec203bb54b63"
+dependencies = [
+ "bitflags",
+ "gdk",
+ "gdk-pixbuf",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gdkx11-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango",
+ "x11",
+]
+
+[[package]]
+name = "gdkx11-sys"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6710388d530f3178ccbeb65cbafdf497a5772c4409eaf574ee9fa461af0a3d09"
+dependencies = [
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+ "x11",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e692e296bfac1d2533ef168d0b60ff5897b8b70a4009276834014dd8924cc028"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -804,6 +855,18 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "nix"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 0.1.10",
+ "libc",
+]
+
[[package]]
name = "nix"
version = "0.19.1"
@@ -1627,12 +1690,43 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "winapi-wsapoll"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "x11"
+version = "2.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "232bc353846d5350d7a815c249209f9545f1d5c725dca846a5ae383a70f30fa5"
+dependencies = [
+ "gethostname",
+ "nix 0.18.0",
+ "winapi",
+ "winapi-wsapoll",
+]
+
[[package]]
name = "xmlparser"
version = "0.13.3"
diff --git a/Cargo.toml b/Cargo.toml
index d5dd536..2f60722 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,12 @@ repository = "https://github.com/elkowar/eww"
homepage = "https://github.com/elkowar/eww"
+[features]
+default = ["x11"]
+x11 = ["gdkx11", "x11rb"]
+no-x11 = []
+
+
[dependencies]
gtk = { version = "0.9", features = [ "v3_16" ] }
gdk = { version = "", features = ["v3_16"] }
@@ -17,6 +23,9 @@ glib = { version = "", features = ["v2_44"] }
gdk-pixbuf = "0.9"
+gdkx11 = { version = "0.9", optional = true }
+x11rb = { version = "0.7", features = ["randr"], optional = true }
+
regex = "1"
bincode = "1.3"
anyhow = "1.0"
@@ -47,5 +56,10 @@ futures-util = "0.3"
inotify = "0.9"
tokio-util = "0.6"
+
+[target.'cfg(target_os="linux")'.dependencies]
+inotify = "0.9"
+
+
[dev-dependencies]
pretty_assertions = "0.6.1"
diff --git a/examples/eww-bar/eww.xml b/examples/eww-bar/eww.xml
index eb70a81..47a1a08 100644
--- a/examples/eww-bar/eww.xml
+++ b/examples/eww-bar/eww.xml
@@ -22,15 +22,15 @@ contain are defined. -->
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -45,7 +45,7 @@ contain are defined. -->
-
+
@@ -66,11 +66,11 @@ contain are defined. -->
-
+
playerctl metadata --format '{{ artist }} - {{ title }}'
-
+
@@ -93,8 +93,9 @@ contain are defined. -->
-
+
+
diff --git a/src/app.rs b/src/app.rs
index 7a0d8e5..045b1a7 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,7 +1,7 @@
use crate::{
config,
config::{window_definition::WindowName, AnchorPoint, WindowStacking},
- eww_state,
+ display_backend, eww_state,
script_var_handler::*,
value::{AttrValue, Coords, NumWithUnit, PrimitiveValue, VarName},
widgets,
@@ -379,6 +379,8 @@ fn initialize_window(
window.set_keep_below(true);
}
+ display_backend::reserve_space_for(&window, monitor_geometry, window_def.struts)?;
+
Ok(EwwWindow {
name: window_def.name.clone(),
definition: window_def,
diff --git a/src/config/window_definition.rs b/src/config/window_definition.rs
index 0863dc2..3a178c4 100644
--- a/src/config/window_definition.rs
+++ b/src/config/window_definition.rs
@@ -1,4 +1,4 @@
-use crate::ensure_xml_tag_is;
+use crate::{ensure_xml_tag_is, value::NumWithUnit};
use anyhow::*;
use derive_more::*;
use serde::{Deserialize, Serialize};
@@ -6,6 +6,21 @@ use smart_default::SmartDefault;
use super::*;
+#[derive(Debug, Clone, Copy, Eq, PartialEq, smart_default::SmartDefault)]
+pub enum Side {
+ #[default]
+ Top,
+ Left,
+ Right,
+ Bottom,
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
+pub struct StrutDefinition {
+ pub side: Side,
+ pub dist: NumWithUnit,
+}
+
#[derive(Debug, Clone, PartialEq)]
pub struct EwwWindowDefinition {
pub name: WindowName,
@@ -13,7 +28,7 @@ pub struct EwwWindowDefinition {
pub stacking: WindowStacking,
pub screen_number: Option,
pub widget: WidgetUse,
- pub struts: Struts,
+ pub struts: StrutDefinition,
pub focusable: bool,
}
@@ -21,10 +36,17 @@ impl EwwWindowDefinition {
pub fn from_xml_element(xml: &XmlElement) -> Result {
ensure_xml_tag_is!(xml, "window");
let stacking: WindowStacking = xml.parse_optional_attr("stacking")?.unwrap_or_default();
- let screen_number = xml.parse_optional_attr("screen")?;
- let focusable = xml.parse_optional_attr("focusable")?;
- let struts = xml.child("struts").ok().map(Struts::from_xml_element).transpose()?;
+ // TODO maybe rename this to monitor?
+ let focusable = xml.parse_optional_attr("focusable")?;
+ let screen_number = xml.parse_optional_attr("screen")?;
+
+ let struts: Option = xml
+ .child("reserve")
+ .ok()
+ .map(parse_strut_definition)
+ .transpose()
+ .context("Failed to parse ")?;
Ok(EwwWindowDefinition {
name: WindowName(xml.attr("name")?.to_owned()),
@@ -46,23 +68,23 @@ impl EwwWindowDefinition {
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct Struts {
- left: i32,
- right: i32,
- top: i32,
- bottom: i32,
+fn parse_strut_definition(xml: XmlElement) -> Result {
+ Ok(StrutDefinition {
+ side: parse_side(xml.attr("side")?)?,
+ dist: xml.attr("distance")?.parse()?,
+ })
}
-impl Struts {
- pub fn from_xml_element(xml: XmlElement) -> Result {
- ensure_xml_tag_is!(xml, "struts");
- Ok(Struts {
- left: xml.attr("left")?.parse()?,
- right: xml.attr("right")?.parse()?,
- top: xml.attr("top")?.parse()?,
- bottom: xml.attr("bottom")?.parse()?,
- })
+fn parse_side(s: &str) -> Result {
+ match s {
+ "l" | "left" => Ok(Side::Left),
+ "r" | "right" => Ok(Side::Right),
+ "t" | "top" => Ok(Side::Top),
+ "b" | "bottom" => Ok(Side::Bottom),
+ _ => Err(anyhow!(
+ "Failed to parse {} as valid side. Must be one of \"left\", \"right\", \"top\", \"bottom\"",
+ s
+ )),
}
}
diff --git a/src/display_backend.rs b/src/display_backend.rs
new file mode 100644
index 0000000..1eecdb1
--- /dev/null
+++ b/src/display_backend.rs
@@ -0,0 +1,131 @@
+pub use platform::*;
+
+#[cfg(feature = "no-x11")]
+mod platform {
+ pub fn reserve_space_for(window: >k::Window, monitor: gdk::Rectangle, strut_def: StrutDefinition) -> Result<()> {
+ Err(anyhow!("Cannot reserve space on non-X11 backends"))
+ }
+}
+
+#[cfg(feature = "x11")]
+mod platform {
+ use crate::config::{Side, StrutDefinition};
+ use anyhow::*;
+ use gdkx11;
+ use gtk::{self, prelude::*};
+ use x11rb::protocol::xproto::ConnectionExt;
+
+ use x11rb::{
+ self,
+ connection::Connection,
+ protocol::xproto::*,
+ rust_connection::{DefaultStream, RustConnection},
+ };
+
+ pub fn reserve_space_for(window: >k::Window, monitor: gdk::Rectangle, strut_def: StrutDefinition) -> Result<()> {
+ let backend = X11Backend::new()?;
+ backend.reserve_space_for(window, monitor, strut_def)?;
+ Ok(())
+ }
+
+ struct X11Backend {
+ conn: RustConnection,
+ root_window: u32,
+ atoms: AtomCollection,
+ }
+
+ impl X11Backend {
+ fn new() -> Result {
+ let (conn, screen_num) = RustConnection::connect(None)?;
+ let screen = conn.setup().roots[screen_num].clone();
+ let atoms = AtomCollection::new(&conn)?.reply()?;
+ Ok(X11Backend {
+ conn,
+ root_window: screen.root,
+ atoms,
+ })
+ }
+
+ fn reserve_space_for(
+ &self,
+ window: >k::Window,
+ monitor_rect: gdk::Rectangle,
+ strut_def: StrutDefinition,
+ ) -> Result<()> {
+ let win_id = window
+ .get_window()
+ .context("Couldn't get gdk window from gtk window")?
+ .downcast::()
+ .ok()
+ .context("Failed to get x11 window for gtk window")?
+ .get_xid() as u32;
+ let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;
+
+ let mon_end_x = (monitor_rect.x + monitor_rect.width) as u32 - 1u32;
+ let mon_end_y = (monitor_rect.y + monitor_rect.height) as u32 - 1u32;
+
+ let dist = match strut_def.side {
+ Side::Left | Side::Right => strut_def.dist.relative_to(monitor_rect.width) as u32,
+ Side::Top | Side::Bottom => strut_def.dist.relative_to(monitor_rect.height) as u32,
+ };
+
+ // don't question it,.....
+ // it's how the X gods want it to be.
+ // left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x
+ #[rustfmt::skip]
+ let strut_list: Vec = match strut_def.side {
+ Side::Left => vec![dist + monitor_rect.x as u32, 0, 0, 0, monitor_rect.y as u32, mon_end_y, 0, 0, 0, 0, 0, 0],
+ Side::Right => vec![0, root_window_geometry.width as u32 - mon_end_x + dist, 0, 0, 0, 0, monitor_rect.y as u32, mon_end_y, 0, 0, 0, 0],
+ Side::Top => vec![0, 0, dist + monitor_rect.y as u32, 0, 0, 0, 0, 0, monitor_rect.x as u32, mon_end_x, 0, 0],
+ Side::Bottom => vec![0, 0, 0, root_window_geometry.height as u32 - mon_end_y + dist, 0, 0, 0, 0, 0, 0, monitor_rect.x as u32, mon_end_x]
+ }.iter().flat_map(|x| x.to_le_bytes().to_vec()).collect();
+
+ self.conn
+ .change_property(
+ PropMode::Replace,
+ win_id,
+ self.atoms._NET_WM_STRUT,
+ self.atoms.CARDINAL,
+ 32,
+ 4,
+ &strut_list[0..16],
+ )?
+ .check()?;
+ self.conn
+ .change_property(
+ PropMode::Replace,
+ win_id,
+ self.atoms._NET_WM_STRUT_PARTIAL,
+ self.atoms.CARDINAL,
+ 32,
+ 12,
+ &strut_list,
+ )?
+ .check()?;
+ self.conn.flush()?;
+ Ok(())
+ }
+ }
+
+ x11rb::atom_manager! {
+ pub AtomCollection: AtomCollectionCookie {
+ _NET_WM_WINDOW_TYPE,
+ _NET_WM_WINDOW_TYPE_DOCK,
+ _NET_WM_WINDOW_TYPE_DIALOG,
+ _NET_WM_STATE,
+ _NET_WM_STATE_STICKY,
+ _NET_WM_STATE_ABOVE,
+ _NET_WM_STATE_BELOW,
+ _NET_WM_NAME,
+ _NET_WM_STRUT,
+ _NET_WM_STRUT_PARTIAL,
+ WM_NAME,
+ UTF8_STRING,
+ COMPOUND_TEXT,
+ CARDINAL,
+ ATOM,
+ WM_CLASS,
+ STRING,
+ }
+ }
+}
diff --git a/src/geometry.rs b/src/geometry.rs
new file mode 100644
index 0000000..d114234
--- /dev/null
+++ b/src/geometry.rs
@@ -0,0 +1,36 @@
+use derive_more::*;
+pub trait Rectangular {
+ fn get_rect(&self) -> Rect;
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Display)]
+#[display(fmt = ".x*.y:.width*.height")]
+pub struct Rect {
+ pub x: i32,
+ pub y: i32,
+ pub width: i32,
+ pub height: i32,
+}
+
+impl Rect {
+ pub fn of(x: i32, y: i32, width: i32, height: i32) -> Self {
+ Rect { x, y, width, height }
+ }
+}
+
+impl Rectangular for Rect {
+ fn get_rect(&self) -> Rect {
+ *self
+ }
+}
+
+impl Rectangular for gdk::Rectangle {
+ fn get_rect(&self) -> Rect {
+ Rect {
+ x: self.x,
+ y: self.y,
+ width: self.width,
+ height: self.height,
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 50305b7..8760588 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,6 +24,8 @@ pub mod server;
pub mod util;
pub mod value;
pub mod widgets;
+pub mod geometry;
+pub mod display_backend;
lazy_static::lazy_static! {
pub static ref IPC_SOCKET_PATH: std::path::PathBuf = std::env::var("XDG_RUNTIME_DIR")
diff --git a/src/opts.rs b/src/opts.rs
index 09d62fc..e4004ef 100644
--- a/src/opts.rs
+++ b/src/opts.rs
@@ -28,7 +28,7 @@ struct RawOpt {
#[derive(StructOpt, Debug, Serialize, Deserialize, PartialEq)]
pub enum Action {
/// Start the Eww daemon.
- #[structopt(name = "daemon")]
+ #[structopt(name = "daemon", alias = "d")]
Daemon {
/// Custom Config Path
#[structopt(short, long)]
@@ -56,7 +56,7 @@ pub enum ActionWithServer {
Ping,
/// Update the value of a variable, in a running eww instance
- #[structopt(name = "update")]
+ #[structopt(name = "update", alias = "u")]
Update {
/// variable_name="new_value"-pairs that will be updated
#[structopt(parse(try_from_str = parse_var_update_arg))]
@@ -64,7 +64,7 @@ pub enum ActionWithServer {
},
/// open a window
- #[structopt(name = "open")]
+ #[structopt(name = "open", alias = "o")]
OpenWindow {
/// Name of the window you want to open.
window_name: WindowName,
@@ -88,19 +88,19 @@ pub enum ActionWithServer {
OpenMany { windows: Vec },
/// Close the window with the given name
- #[structopt(name = "close")]
+ #[structopt(name = "close", alias = "c")]
CloseWindow { window_name: WindowName },
/// Reload the configuration
- #[structopt(name = "reload")]
+ #[structopt(name = "reload", alias = "r")]
Reload,
/// kill the eww daemon
- #[structopt(name = "kill")]
+ #[structopt(name = "kill", alias = "k")]
KillServer,
/// Close all windows, without killing the daemon
- #[structopt(name = "close-all")]
+ #[structopt(name = "close-all", alias = "ca")]
CloseAll,
/// Print the current eww-state
diff --git a/src/value/coords.rs b/src/value/coords.rs
index 04348c6..e017ace 100644
--- a/src/value/coords.rs
+++ b/src/value/coords.rs
@@ -15,6 +15,15 @@ pub enum NumWithUnit {
Pixels(i32),
}
+impl NumWithUnit {
+ pub fn relative_to(&self, max: i32) -> i32 {
+ match *self {
+ NumWithUnit::Percent(n) => ((max as f64 / 100.0) * n as f64) as i32,
+ NumWithUnit::Pixels(n) => n,
+ }
+ }
+}
+
impl FromStr for NumWithUnit {
type Err = anyhow::Error;
@@ -35,7 +44,7 @@ impl FromStr for NumWithUnit {
}
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Display, Default)]
-#[display(fmt = "{}X{}", x, y)]
+#[display(fmt = "{}*{}", x, y)]
pub struct Coords {
pub x: NumWithUnit,
pub y: NumWithUnit,
@@ -46,7 +55,7 @@ impl FromStr for Coords {
fn from_str(s: &str) -> Result {
let (x, y) = s
- .split_once(|x: char| x.to_ascii_lowercase() == 'x')
+ .split_once(|x: char| x.to_ascii_lowercase() == 'x' || x.to_ascii_lowercase() == '*')
.ok_or_else(|| anyhow!("must be formatted like 200x500"))?;
Coords::from_strs(x, y)
}
@@ -59,6 +68,13 @@ impl fmt::Debug for Coords {
}
impl Coords {
+ pub fn from_pixels(x: i32, y: i32) -> Self {
+ Coords {
+ x: NumWithUnit::Pixels(x),
+ y: NumWithUnit::Pixels(y),
+ }
+ }
+
/// parse a string for x and a string for y into a [`Coords`] object.
pub fn from_strs(x: &str, y: &str) -> Result {
Ok(Coords {
@@ -69,16 +85,7 @@ impl Coords {
/// resolve the possibly relative coordinates relative to a given containers size
pub fn relative_to(&self, width: i32, height: i32) -> (i32, i32) {
- (
- match self.x {
- NumWithUnit::Percent(n) => ((width as f64 / 100.0) * n as f64) as i32,
- NumWithUnit::Pixels(n) => n,
- },
- match self.y {
- NumWithUnit::Percent(n) => ((height as f64 / 100.0) * n as f64) as i32,
- NumWithUnit::Pixels(n) => n,
- },
- )
+ (self.x.relative_to(width), self.y.relative_to(height))
}
}