feat: Add support for on-demand window focus on wayland (#1215)

* feat: Add support for on-demand window focus on wayland

* fix: cargo fmt

* Update CHANGELOG.md with OnDemand focusable

* fix: add v0_6 feature to gtk-layer-shell in eww/cargo.toml
This commit is contained in:
GallowsDove 2024-12-27 00:44:06 +01:00 committed by GitHub
parent 95124ad274
commit 2c81b3fbc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 52 additions and 19 deletions

View file

@ -18,6 +18,7 @@ All notable changes to eww will be listed here, starting at changes since versio
- Fix wayland monitor names support (By: dragonnn) - Fix wayland monitor names support (By: dragonnn)
### Features ### Features
- Add OnDemand support for focusable on wayland (By: GallowsDove)
- Update rust toolchain to 1.81.0 (By: w-lfchen) - Update rust toolchain to 1.81.0 (By: w-lfchen)
- Add `:fill-svg` and `:preserve-aspect-ratio` properties to images (By: hypernova7, w-lfchen) - Add `:fill-svg` and `:preserve-aspect-ratio` properties to images (By: hypernova7, w-lfchen)
- Add `:truncate` property to labels, disabled by default (except in cases where truncation would be enabled in version `0.5.0` and before) (By: Rayzeq). - Add `:truncate` property to labels, disabled by default (except in cases where truncation would be enabled in version `0.5.0` and before) (By: Rayzeq).

View file

@ -20,7 +20,7 @@ eww_shared_util.workspace = true
yuck.workspace = true yuck.workspace = true
notifier_host.workspace = true notifier_host.workspace = true
gtk-layer-shell = { version = "0.8.1", optional = true } gtk-layer-shell = { version = "0.8.1", optional = true, features=["v0_6"] }
gdkx11 = { version = "0.18", optional = true } gdkx11 = { version = "0.18", optional = true }
x11rb = { version = "0.13.1", features = ["randr"], optional = true } x11rb = { version = "0.13.1", features = ["randr"], optional = true }
gdk-sys = "0.18.0" gdk-sys = "0.18.0"

View file

@ -28,14 +28,14 @@ impl DisplayBackend for NoBackend {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
mod platform_wayland { mod platform_wayland {
use super::DisplayBackend;
use crate::{widgets::window::Window, window_initiator::WindowInitiator}; use crate::{widgets::window::Window, window_initiator::WindowInitiator};
use gtk::gdk; use gtk::gdk;
use gtk::prelude::*; use gtk::prelude::*;
use gtk_layer_shell::LayerShell; use gtk_layer_shell::{KeyboardMode, LayerShell};
use yuck::config::backend_window_options::WlWindowFocusable;
use yuck::config::{window_definition::WindowStacking, window_geometry::AnchorAlignment}; use yuck::config::{window_definition::WindowStacking, window_geometry::AnchorAlignment};
use super::DisplayBackend;
pub struct WaylandBackend; pub struct WaylandBackend;
impl DisplayBackend for WaylandBackend { impl DisplayBackend for WaylandBackend {
@ -70,7 +70,11 @@ mod platform_wayland {
} }
// Sets the keyboard interactivity // Sets the keyboard interactivity
window.set_keyboard_interactivity(window_init.backend_options.wayland.focusable); match window_init.backend_options.wayland.focusable {
WlWindowFocusable::None => window.set_keyboard_mode(KeyboardMode::None),
WlWindowFocusable::Exclusive => window.set_keyboard_mode(KeyboardMode::Exclusive),
WlWindowFocusable::OnDemand => window.set_keyboard_mode(KeyboardMode::OnDemand),
}
if let Some(geometry) = window_init.geometry { if let Some(geometry) = window_init.geometry {
// Positioning surface // Positioning surface

View file

@ -7,6 +7,7 @@ use simplexpr::{
SimplExpr, SimplExpr,
}; };
use super::{attributes::Attributes, window_definition::EnumParseError};
use crate::{ use crate::{
enum_parse, enum_parse,
error::DiagResult, error::DiagResult,
@ -14,8 +15,7 @@ use crate::{
value::{coords, NumWithUnit}, value::{coords, NumWithUnit},
}; };
use eww_shared_util::{Span, VarName}; use eww_shared_util::{Span, VarName};
use simplexpr::dynval::ConversionError;
use super::{attributes::Attributes, window_definition::EnumParseError};
use crate::error::{DiagError, DiagResultExt}; use crate::error::{DiagError, DiagResultExt};
@ -27,6 +27,8 @@ pub enum Error {
CoordsError(#[from] coords::Error), CoordsError(#[from] coords::Error),
#[error(transparent)] #[error(transparent)]
EvalError(#[from] EvalError), EvalError(#[from] EvalError),
#[error(transparent)]
ConversionError(#[from] ConversionError),
} }
/// Backend-specific options of a window /// Backend-specific options of a window
@ -45,6 +47,7 @@ impl BackendWindowOptionsDef {
pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> { pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> {
let struts = attrs.ast_optional("reserve")?; let struts = attrs.ast_optional("reserve")?;
let window_type = attrs.ast_optional("windowtype")?; let window_type = attrs.ast_optional("windowtype")?;
let focusable = attrs.ast_optional("focusable")?;
let x11 = X11BackendWindowOptionsDef { let x11 = X11BackendWindowOptionsDef {
sticky: attrs.ast_optional("sticky")?, sticky: attrs.ast_optional("sticky")?,
struts, struts,
@ -53,7 +56,7 @@ impl BackendWindowOptionsDef {
}; };
let wayland = WlBackendWindowOptionsDef { let wayland = WlBackendWindowOptionsDef {
exclusive: attrs.ast_optional("exclusive")?, exclusive: attrs.ast_optional("exclusive")?,
focusable: attrs.ast_optional("focusable")?, focusable,
namespace: attrs.ast_optional("namespace")?, namespace: attrs.ast_optional("namespace")?,
}; };
@ -109,7 +112,7 @@ impl X11BackendWindowOptionsDef {
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct WlBackendWindowOptions { pub struct WlBackendWindowOptions {
pub exclusive: bool, pub exclusive: bool,
pub focusable: bool, pub focusable: WlWindowFocusable,
pub namespace: Option<String>, pub namespace: Option<String>,
} }
@ -122,10 +125,13 @@ pub struct WlBackendWindowOptionsDef {
} }
impl WlBackendWindowOptionsDef { impl WlBackendWindowOptionsDef {
fn eval(&self, local_variables: &HashMap<VarName, DynVal>) -> Result<WlBackendWindowOptions, EvalError> { fn eval(&self, local_variables: &HashMap<VarName, DynVal>) -> Result<WlBackendWindowOptions, Error> {
Ok(WlBackendWindowOptions { Ok(WlBackendWindowOptions {
exclusive: eval_opt_expr_as_bool(&self.exclusive, false, local_variables)?, exclusive: eval_opt_expr_as_bool(&self.exclusive, false, local_variables)?,
focusable: eval_opt_expr_as_bool(&self.focusable, false, local_variables)?, focusable: match &self.focusable {
Some(expr) => WlWindowFocusable::from_dynval(&expr.eval(local_variables)?)?,
None => WlWindowFocusable::default(),
},
namespace: match &self.namespace { namespace: match &self.namespace {
Some(expr) => Some(expr.eval(local_variables)?.as_string()?), Some(expr) => Some(expr.eval(local_variables)?.as_string()?),
None => None, None => None,
@ -145,6 +151,28 @@ fn eval_opt_expr_as_bool(
}) })
} }
#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)]
pub enum WlWindowFocusable {
#[default]
None,
Exclusive,
OnDemand,
}
impl FromStr for WlWindowFocusable {
type Err = EnumParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
enum_parse! { "focusable", s,
"none" => Self::None,
"exclusive" => Self::Exclusive,
"ondemand" => Self::OnDemand,
// legacy support
"true" => Self::Exclusive,
"false" => Self::None,
}
}
}
/// Window type of an x11 window /// Window type of an x11 window
#[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)] #[derive(Debug, Clone, PartialEq, Eq, smart_default::SmartDefault, serde::Serialize)]
pub enum X11WindowType { pub enum X11WindowType {
@ -182,7 +210,7 @@ pub enum Side {
Bottom, Bottom,
} }
impl std::str::FromStr for Side { impl FromStr for Side {
type Err = EnumParseError; type Err = EnumParseError;
fn from_str(s: &str) -> Result<Side, Self::Err> { fn from_str(s: &str) -> Result<Side, Self::Err> {

View file

@ -86,12 +86,12 @@ Depending on if you are using X11 or Wayland, some additional properties exist:
#### Wayland #### Wayland
| Property | Description | | Property | Description |
| ----------: | ------------------------------------------------------------ | | ----------: |------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`, `overlay`, `bottom`. | | `stacking` | Where the window should appear in the stack. Possible values: `fg`, `bg`, `overlay`, `bottom`. |
| `exclusive` | Whether the compositor should reserve space for the window automatically. Either `true` or `false`. | | `exclusive` | Whether the compositor should reserve space for the window automatically. Either `true` or `false`. |
| `focusable` | Whether the window should be able to be focused. This is necessary for any widgets that use the keyboard to work. Either `true` or `false`. | | `focusable` | Whether the window should be able to be focused. This is necessary for any widgets that use the keyboard to work. Possible values: `none`, `exclusive` and `ondemand`. |
| `namespace` | Set the wayland layersurface namespace eww uses. Accepts a `string` value. | | `namespace` | Set the wayland layersurface namespace eww uses. Accepts a `string` value. |

View file

@ -65,7 +65,7 @@
(defwindow data-structures (defwindow data-structures
:monitor 0 :monitor 0
:exclusive false :exclusive false
:focusable false :focusable none
:geometry (geometry :geometry (geometry
:anchor "center" :anchor "center"
) )