1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

x11/wayland: subscribe to xdg desktop portal for settings changes

We use this to detect changes in dark mode

refs: https://github.com/wez/wezterm/issues/2258
This commit is contained in:
Wez Furlong 2022-07-13 18:48:25 -07:00
parent 5f8ed00faf
commit a1c8b4a9b3
8 changed files with 108 additions and 33 deletions

View File

@ -27,7 +27,7 @@ As features stabilize some brief notes about them will accumulate here.
* New [wezterm.json_parse()](config/lua/wezterm/json_parse.md) and [wezterm.json_encode()](config/lua/wezterm/json_encode.md) functions for working with JSON. * New [wezterm.json_parse()](config/lua/wezterm/json_parse.md) and [wezterm.json_encode()](config/lua/wezterm/json_encode.md) functions for working with JSON.
* Hundreds of new color schemes have been imported from [base16](https://github.com/chriskempson/base16-schemes-source), [Gogh](https://gogh-co.github.io/Gogh/) and [terminal.sexy](https://terminal.sexy/). [Browse the schemes](colorschemes/index.md) and look for themes with `(base16)`, `(Gogh)` and `(terminal.sexy)` in the name to discover them! * Hundreds of new color schemes have been imported from [base16](https://github.com/chriskempson/base16-schemes-source), [Gogh](https://gogh-co.github.io/Gogh/) and [terminal.sexy](https://terminal.sexy/). [Browse the schemes](colorschemes/index.md) and look for themes with `(base16)`, `(Gogh)` and `(terminal.sexy)` in the name to discover them!
* [pane:is_alt_screen_active()](config/lua/pane/is_alt_screen_active.md) for testing whether the alt screen is active. Thanks to [@Funami580](https://github.com/Funami580)! [#2234](https://github.com/wez/wezterm/issues/2234) * [pane:is_alt_screen_active()](config/lua/pane/is_alt_screen_active.md) for testing whether the alt screen is active. Thanks to [@Funami580](https://github.com/Funami580)! [#2234](https://github.com/wez/wezterm/issues/2234)
* X11/Wayland: XDG desktop portal is now used to determine whether dark mode is in use [#2258](https://github.com/wez/wezterm/issues/2258) * X11/Wayland: [XDG desktop portal](https://flatpak.github.io/xdg-desktop-portal/) is now used to determine whether dark mode is in use [#2258](https://github.com/wez/wezterm/issues/2258)
#### Fixed #### Fixed
* [ActivateKeyTable](config/lua/keyassignment/ActivateKeyTable.md)'s `replace_current` field was not actually optional. Made it optional. [#2179](https://github.com/wez/wezterm/issues/2179) * [ActivateKeyTable](config/lua/keyassignment/ActivateKeyTable.md)'s `replace_current` field was not actually optional. Made it optional. [#2179](https://github.com/wez/wezterm/issues/2179)

View File

@ -7,16 +7,12 @@ can be one of the following 4 values:
* `"Light"` - the normal appearance, with dark text on a light background * `"Light"` - the normal appearance, with dark text on a light background
* `"Dark"` - "dark mode", with predominantly dark colors and probably a lighter, lower contrasting, text color on a dark background * `"Dark"` - "dark mode", with predominantly dark colors and probably a lighter, lower contrasting, text color on a dark background
* `"LightHighContrast"` - light mode but with high contrast colors * `"LightHighContrast"` - light mode but with high contrast colors (not reported on all systems)
* `"DarkHighContrast"` - dark mode but with high contrast colors * `"DarkHighContrast"` - dark mode but with high contrast colors (not reported on all systems)
wezterm currently doesn't know how to interrogate the appearance on Wayland wezterm is able to detect when the appearance has changed and will generate a
systems, and will always report `"Light"`. [window-config-reloaded](../window-events/window-config-reloaded.md) event for
each window.
On macOS, X11 and Windows systems, wezterm is able to detect when the
appearance has changed and will generate a
[window-config-reloaded](../window-events/window-config-reloaded.md) event for each
window.
This example configuration shows how you can have your color scheme This example configuration shows how you can have your color scheme
automatically adjust to the current appearance: automatically adjust to the current appearance:
@ -48,6 +44,16 @@ return {
### Wayland GNOME Appearance ### Wayland GNOME Appearance
*Since: nightly builds only*
wezterm uses [XDG Desktop
Portal](https://flatpak.github.io/xdg-desktop-portal/) to determine the
appearance.
In earlier versions you may wish to use an alternative method to determine the
appearance, as wezterm didn't know how to interrogate the appearance on Wayland
systems, and would always report `"Light"`.
The GNOME desktop environment provides the `gsettings` tool that can The GNOME desktop environment provides the `gsettings` tool that can
inform us of the selected appearance even in a Wayland session. We can inform us of the selected appearance even in a Wayland session. We can
substitute the call to `window:get_appearance` above with a call to the substitute the call to `window:get_appearance` above with a call to the
@ -77,10 +83,10 @@ function query_appearance_gnome()
end end
``` ```
Since WezTerm will not fire a `window-config-reloaded` Since WezTerm will not fire a `window-config-reloaded` event on Wayland for
event on Wayland, you will instead need to listen on the older versions of wezterm, you will instead need to listen on the
[update-right-status](../window-events/update-right-status.md) event, [update-right-status](../window-events/update-right-status.md) event, which
which will essentially poll for the appearance periodically: will essentially poll for the appearance periodically:
```lua ```lua
local wezterm = require 'wezterm' local wezterm = require 'wezterm'

View File

@ -412,6 +412,12 @@ impl WaylandConnection {
} }
Ok(()) Ok(())
} }
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
for win in self.windows.borrow().values() {
win.borrow_mut().appearance_changed(appearance);
}
}
} }
impl ConnectionOps for WaylandConnection { impl ConnectionOps for WaylandConnection {

View File

@ -6,9 +6,9 @@ use crate::os::wayland::connection::WaylandConnection;
use crate::os::wayland::wl_id; use crate::os::wayland::wl_id;
use crate::os::x11::keyboard::Keyboard; use crate::os::x11::keyboard::Keyboard;
use crate::{ use crate::{
Clipboard, Connection, Dimensions, MouseCursor, Point, Rect, RequestedWindowGeometry, Appearance, Clipboard, Connection, Dimensions, MouseCursor, Point, Rect,
ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender, WindowKeyEvent, RequestedWindowGeometry, ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender,
WindowOps, WindowState, WindowKeyEvent, WindowOps, WindowState,
}; };
use anyhow::{anyhow, bail, Context}; use anyhow::{anyhow, bail, Context};
use async_io::Timer; use async_io::Timer;
@ -146,6 +146,7 @@ pub struct WaylandWindowInner {
invalidated: bool, invalidated: bool,
font_config: Rc<FontConfiguration>, font_config: Rc<FontConfiguration>,
text_cursor: Option<Rect>, text_cursor: Option<Rect>,
appearance: Appearance,
config: Option<ConfigHandle>, config: Option<ConfigHandle>,
// cache the title for comparison to avoid spamming // cache the title for comparison to avoid spamming
// the compositor with updates that don't actually change it // the compositor with updates that don't actually change it
@ -372,6 +373,7 @@ impl WaylandWindow {
gl_state: None, gl_state: None,
wegl_surface: None, wegl_surface: None,
text_cursor: None, text_cursor: None,
appearance: Appearance::Light,
})); }));
let window_handle = Window::Wayland(WaylandWindow(window_id)); let window_handle = Window::Wayland(WaylandWindow(window_id));
@ -401,6 +403,14 @@ unsafe impl HasRawWindowHandle for WaylandWindowInner {
} }
impl WaylandWindowInner { impl WaylandWindowInner {
pub(crate) fn appearance_changed(&mut self, appearance: Appearance) {
if appearance != self.appearance {
self.appearance = appearance;
self.events
.dispatch(WindowEvent::AppearanceChanged(appearance));
}
}
pub(crate) fn keyboard_event(&mut self, event: WlKeyboardEvent) { pub(crate) fn keyboard_event(&mut self, event: WlKeyboardEvent) {
let conn = WaylandConnection::get().unwrap().wayland(); let conn = WaylandConnection::get().unwrap().wayland();
let mut mapper = conn.keyboard_mapper.borrow_mut(); let mut mapper = conn.keyboard_mapper.borrow_mut();

View File

@ -313,6 +313,12 @@ impl XConnection {
*self.default_dpi.borrow_mut() = dpi; *self.default_dpi.borrow_mut() = dpi;
} }
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
for win in self.windows.borrow().values() {
win.lock().unwrap().appearance_changed(appearance);
}
}
fn process_queued_xcb(&self) -> anyhow::Result<()> { fn process_queued_xcb(&self) -> anyhow::Result<()> {
if let Some(event) = self if let Some(event) = self
.conn .conn

View File

@ -523,11 +523,7 @@ impl XWindowInner {
conn.update_xrm(); conn.update_xrm();
self.check_dpi_and_synthesize_resize(); self.check_dpi_and_synthesize_resize();
let appearance = conn.get_appearance(); let appearance = conn.get_appearance();
if appearance != self.appearance { self.appearance_changed(appearance);
self.appearance = appearance;
self.events
.dispatch(WindowEvent::AppearanceChanged(appearance));
}
} }
if msg.atom() == conn.atom_net_wm_state { if msg.atom() == conn.atom_net_wm_state {
@ -555,6 +551,14 @@ impl XWindowInner {
Ok(()) Ok(())
} }
pub(crate) fn appearance_changed(&mut self, appearance: Appearance) {
if appearance != self.appearance {
self.appearance = appearance;
self.events
.dispatch(WindowEvent::AppearanceChanged(appearance));
}
}
fn focus_changed(&mut self, focused: bool) { fn focus_changed(&mut self, focused: bool) {
log::trace!("focus_changed {focused}, flagging geometry as unsure"); log::trace!("focus_changed {focused}, flagging geometry as unsure");
self.sure_about_geometry = false; self.sure_about_geometry = false;

View File

@ -104,6 +104,15 @@ impl Connection {
_ => panic!("attempted to get wayland reference on non-wayland connection"), _ => panic!("attempted to get wayland reference on non-wayland connection"),
} }
} }
pub(crate) fn advise_of_appearance_change(&self, appearance: Appearance) {
log::trace!("Appearance changed to {appearance:?}");
match self {
Self::X11(x) => x.advise_of_appearance_change(appearance),
#[cfg(feature = "wayland")]
Self::Wayland(w) => w.advise_of_appearance_change(appearance),
}
}
} }
impl ConnectionOps for Connection { impl ConnectionOps for Connection {
@ -124,6 +133,7 @@ impl ConnectionOps for Connection {
} }
fn run_message_loop(&self) -> anyhow::Result<()> { fn run_message_loop(&self) -> anyhow::Result<()> {
crate::os::xdg_desktop_portal::subscribe();
match self { match self {
Self::X11(x) => x.run_message_loop(), Self::X11(x) => x.run_message_loop(),
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@ -132,17 +142,8 @@ impl ConnectionOps for Connection {
} }
fn get_appearance(&self) -> Appearance { fn get_appearance(&self) -> Appearance {
match promise::spawn::block_on(crate::os::xdg_desktop_portal::read_setting( match promise::spawn::block_on(crate::os::xdg_desktop_portal::get_appearance()) {
"org.freedesktop.appearance", Ok(appearance) => return appearance,
"color-scheme",
)) {
Ok(value) => match value.downcast_ref::<u32>() {
Some(1) => return Appearance::Dark,
Some(_) => return Appearance::Light,
None => {
log::debug!("Unable to resolve appearance using xdg-desktop-portal: expected a u32 value but got {value:#?}");
}
},
Err(err) => { Err(err) => {
log::debug!("Unable to resolve appearance using xdg-desktop-portal: {err:#}"); log::debug!("Unable to resolve appearance using xdg-desktop-portal: {err:#}");
} }

View File

@ -2,7 +2,9 @@
//! <https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Settings.xml> //! <https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Settings.xml>
use crate::{Appearance, Connection, ConnectionOps};
use anyhow::Context; use anyhow::Context;
use futures_util::stream::StreamExt;
use std::collections::HashMap; use std::collections::HashMap;
use zbus::dbus_proxy; use zbus::dbus_proxy;
use zvariant::OwnedValue; use zvariant::OwnedValue;
@ -31,3 +33,43 @@ pub async fn read_setting(namespace: &str, key: &str) -> anyhow::Result<OwnedVal
.context("make proxy")?; .context("make proxy")?;
proxy.Read(namespace, key).await.context("Read") proxy.Read(namespace, key).await.context("Read")
} }
fn value_to_appearance(value: OwnedValue) -> anyhow::Result<Appearance> {
Ok(match value.downcast_ref::<u32>() {
Some(1) => Appearance::Dark,
Some(_) => Appearance::Light,
None => {
anyhow::bail!(
"Unable to resolve appearance \
using xdg-desktop-portal: expected a u32 value but got {value:#?}"
);
}
})
}
pub async fn get_appearance() -> anyhow::Result<Appearance> {
let value = read_setting("org.freedesktop.appearance", "color-scheme").await?;
value_to_appearance(value)
}
pub fn subscribe() {
promise::spawn::spawn(async move {
let connection = zbus::ConnectionBuilder::session()?.build().await?;
let proxy = PortalSettingsProxy::new(&connection)
.await
.context("make proxy")?;
let mut stream = proxy.receive_SettingChanged().await?;
while let Some(signal) = stream.next().await {
let args = signal.args()?;
if args.namespace == "org.freedesktop.appearance" && args.key == "color-scheme" {
if let Ok(appearance) = value_to_appearance(args.value) {
let conn =
Connection::get().ok_or_else(|| anyhow::anyhow!("connection is dead"))?;
conn.advise_of_appearance_change(appearance);
}
}
}
Result::<(), anyhow::Error>::Ok(())
})
.detach();
}